@talismn/util 1.0.1 → 2.0.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/dist/index.mjs CHANGED
@@ -1,414 +1,853 @@
1
- // src/addTrailingSlash.ts
2
- var addTrailingSlash = (url) => {
3
- if (url.endsWith("/")) {
4
- return url;
5
- }
6
- return `${url}/`;
1
+ import BigNumber from "bignumber.js";
2
+ import { isEqual } from "lodash-es";
3
+ import { BehaviorSubject, Observable, ReplaySubject, Subject, catchError, concat, concatMap, debounceTime, distinctUntilChanged, from, map, of, shareReplay, skip, startWith, switchMap, take, tap, timer } from "rxjs";
4
+ //#region src/configureBigNumber.ts
5
+ BigNumber.set({ STRICT: false });
6
+ var configureBigNumber_default = BigNumber;
7
+ //#endregion
8
+ //#region src/addTrailingSlash.ts
9
+ const addTrailingSlash = (url) => {
10
+ if (url.endsWith("/")) return url;
11
+ return `${url}/`;
7
12
  };
8
-
9
- // src/BigMath.ts
10
- var BigMath = {
11
- abs(x) {
12
- return x < 0n ? -x : x;
13
- },
14
- sign(x) {
15
- if (x === 0n) return 0n;
16
- return x < 0n ? -1n : 1n;
17
- },
18
- // TODO: Improve our babel/tsc config to let us use the `**` operator on bigint values.
19
- // Error thrown: Exponentiation cannot be performed on 'bigint' values unless the 'target' option is set to 'es2016' or later. ts(2791)
20
- // pow(base: bigint, exponent: bigint) {
21
- // return base ** exponent
22
- // },
23
- min(value, ...values) {
24
- for (const v of values) if (v < value) value = v;
25
- return value;
26
- },
27
- max(value, ...values) {
28
- for (const v of values) if (v > value) value = v;
29
- return value;
30
- }
13
+ //#endregion
14
+ //#region src/assert.ts
15
+ /**
16
+ * @name assert
17
+ * @description Throws an Error with the given message if the condition is falsy.
18
+ * @example
19
+ * assert(chain, "Chain not found")
20
+ **/
21
+ function assert(condition, message) {
22
+ if (condition) return;
23
+ throw new Error(typeof message === "function" ? message() : message);
24
+ }
25
+ //#endregion
26
+ //#region src/BigMath.ts
27
+ /**
28
+ * Javascript's `Math` library for `BigInt`.
29
+ * Taken from https://stackoverflow.com/questions/51867270/is-there-a-library-similar-to-math-that-supports-javascript-bigint/64953280#64953280
30
+ */
31
+ const BigMath = {
32
+ abs(x) {
33
+ return x < 0n ? -x : x;
34
+ },
35
+ sign(x) {
36
+ if (x === 0n) return 0n;
37
+ return x < 0n ? -1n : 1n;
38
+ },
39
+ min(value, ...values) {
40
+ for (const v of values) if (v < value) value = v;
41
+ return value;
42
+ },
43
+ max(value, ...values) {
44
+ for (const v of values) if (v > value) value = v;
45
+ return value;
46
+ }
31
47
  };
32
-
33
- // src/deferred.ts
48
+ //#endregion
49
+ //#region src/isHexString.ts
50
+ const REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
51
+ const isHexString = (value) => {
52
+ return typeof value === "string" && REGEX_HEX_STRING.test(value);
53
+ };
54
+ //#endregion
55
+ //#region src/bytes.ts
56
+ /**
57
+ * Byte / hex primitives, drop-in equivalents of the `@polkadot/util` helpers we used to depend on.
58
+ * Semantics intentionally match polkadot-js for migration safety.
59
+ */
60
+ const hexToU8a = (hex) => {
61
+ if (!hex) return /* @__PURE__ */ new Uint8Array();
62
+ const value = hex.startsWith("0x") ? hex.slice(2) : hex;
63
+ const padded = value.length % 2 ? `${value}0` : value;
64
+ const result = new Uint8Array(padded.length / 2);
65
+ for (let i = 0; i < result.length; i++) result[i] = parseInt(padded.substring(i * 2, i * 2 + 2), 16);
66
+ return result;
67
+ };
68
+ const u8aToHex = (value) => {
69
+ if (!value?.length) return "0x";
70
+ let hex = "";
71
+ for (const byte of value) hex += byte.toString(16).padStart(2, "0");
72
+ return `0x${hex}`;
73
+ };
74
+ const stringToU8a = (value) => value ? new TextEncoder().encode(value) : /* @__PURE__ */ new Uint8Array();
75
+ const u8aToString = (value) => value?.length ? new TextDecoder("utf-8").decode(value) : "";
76
+ const hexToString = (hex) => u8aToString(hexToU8a(hex));
77
+ const hexToNumber = (hex) => hex ? parseInt(hex, 16) : NaN;
78
+ const numberToU8a = (value) => {
79
+ let hex = (value ?? 0).toString(16);
80
+ if (hex.length % 2) hex = `0${hex}`;
81
+ return hexToU8a(hex);
82
+ };
83
+ /** Coerces hex strings, utf-8 strings, number arrays and buffers to Uint8Array (polkadot-js semantics) */
84
+ const u8aToU8a = (value) => {
85
+ if (!value) return /* @__PURE__ */ new Uint8Array();
86
+ if (value instanceof Uint8Array) return value;
87
+ if (Array.isArray(value)) return new Uint8Array(value);
88
+ return isHexString(value) ? hexToU8a(value) : stringToU8a(value);
89
+ };
90
+ const u8aConcat = (...items) => {
91
+ const chunks = items.map((item) => u8aToU8a(item));
92
+ const result = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.length, 0));
93
+ let offset = 0;
94
+ for (const chunk of chunks) {
95
+ result.set(chunk, offset);
96
+ offset += chunk.length;
97
+ }
98
+ return result;
99
+ };
100
+ const u8aEq = (a, b) => {
101
+ const u8aa = u8aToU8a(a);
102
+ const u8ab = u8aToU8a(b);
103
+ if (u8aa.length !== u8ab.length) return false;
104
+ return u8aa.every((byte, i) => byte === u8ab[i]);
105
+ };
106
+ const u8aCmp = (a, b) => {
107
+ const u8aa = u8aToU8a(a);
108
+ const u8ab = u8aToU8a(b);
109
+ for (let i = 0; i < Math.max(u8aa.length, u8ab.length); i++) {
110
+ const byteA = u8aa[i] ?? 0;
111
+ const byteB = u8ab[i] ?? 0;
112
+ if (byteA !== byteB) return byteA < byteB ? -1 : 1;
113
+ }
114
+ return u8aa.length === u8ab.length ? 0 : u8aa.length < u8ab.length ? -1 : 1;
115
+ };
116
+ /**
117
+ * Tests if the input is printable ASCII (code points 32-126). Hex strings are decoded to bytes
118
+ * first, other strings are checked character-wise (polkadot-js `isAscii` semantics).
119
+ */
120
+ const isAsciiPrintable = (value) => {
121
+ if (value == null) return false;
122
+ return (typeof value === "string" && !isHexString(value) ? stringToU8a(value) : u8aToU8a(value)).every((b) => b >= 32 && b <= 126);
123
+ };
124
+ const U8A_WRAP_PREFIX = stringToU8a("<Bytes>");
125
+ const U8A_WRAP_POSTFIX = stringToU8a("</Bytes>");
126
+ const u8aIsWrapped = (value) => value.length >= U8A_WRAP_PREFIX.length + U8A_WRAP_POSTFIX.length && u8aEq(value.subarray(0, U8A_WRAP_PREFIX.length), U8A_WRAP_PREFIX) && u8aEq(value.subarray(value.length - U8A_WRAP_POSTFIX.length), U8A_WRAP_POSTFIX);
127
+ /** Wraps bytes with `<Bytes>...</Bytes>` for raw-message signing, unless already wrapped */
128
+ const u8aWrapBytes = (value) => {
129
+ const u8a = u8aToU8a(value);
130
+ return u8aIsWrapped(u8a) ? u8a : u8aConcat(U8A_WRAP_PREFIX, u8a, U8A_WRAP_POSTFIX);
131
+ };
132
+ const u8aUnwrapBytes = (value) => {
133
+ const u8a = u8aToU8a(value);
134
+ return u8aIsWrapped(u8a) ? u8a.subarray(U8A_WRAP_PREFIX.length, u8a.length - U8A_WRAP_POSTFIX.length) : u8a;
135
+ };
136
+ //#endregion
137
+ //#region src/jsActivityHook.ts
138
+ const reportJsActivity = (label, durationMs) => {
139
+ const record = globalThis.__recordJsActivity;
140
+ if (record) record(label, durationMs);
141
+ };
142
+ //#endregion
143
+ //#region src/yieldToEventLoop.ts
144
+ const yieldToEventLoop = () => {
145
+ if (typeof MessageChannel !== "undefined") return new Promise((resolve) => {
146
+ const channel = new MessageChannel();
147
+ const port1 = channel.port1;
148
+ const port2 = channel.port2;
149
+ port1.onmessage = () => {
150
+ port1.close();
151
+ port2.close();
152
+ resolve();
153
+ };
154
+ port2.postMessage(null);
155
+ });
156
+ return new Promise((resolve) => setTimeout(resolve, 0));
157
+ };
158
+ //#endregion
159
+ //#region src/timeSlicer.ts
160
+ /**
161
+ * Default CPU budget for a single synchronous slice of chunked work.
162
+ * ~50-60% of a 60fps frame, leaving the rest for the host (user interactions, rendering).
163
+ */
164
+ const DEFAULT_TIME_SLICE_BUDGET_MS = 10;
165
+ /**
166
+ * Creates an AbortError-style error without relying on DOMException (not available on Hermes).
167
+ * Recognized by `isAbortError` from this package.
168
+ */
169
+ const newAbortError = () => Object.assign(/* @__PURE__ */ new Error("Aborted"), { name: "AbortError" });
170
+ const defaultNow = typeof performance !== "undefined" ? () => performance.now() : () => Date.now();
171
+ /**
172
+ * Tracks a time budget for cooperative scheduling: run synchronous work until the budget
173
+ * for the current slice is exhausted, then yield the thread back to the host event loop
174
+ * before continuing.
175
+ *
176
+ * A single slicer can be shared across multiple phases of work so the budget spans them.
177
+ */
178
+ const createTimeSlicer = ({ budgetMs = 10, signal, now = defaultNow, label } = {}) => {
179
+ let sliceStart = now();
180
+ const throwIfAborted = () => {
181
+ if (signal?.aborted) throw newAbortError();
182
+ };
183
+ const shouldYield = () => now() - sliceStart >= budgetMs;
184
+ const doYield = async () => {
185
+ const elapsed = now() - sliceStart;
186
+ if (elapsed >= budgetMs * 3) reportJsActivity(`timeSlicer over-budget slice${label ? ` (${label})` : ""}`, elapsed);
187
+ await yieldToEventLoop();
188
+ sliceStart = now();
189
+ throwIfAborted();
190
+ };
191
+ return {
192
+ signal,
193
+ shouldYield,
194
+ yield: doYield,
195
+ yieldIfNeeded: () => {
196
+ throwIfAborted();
197
+ return shouldYield() ? doYield() : void 0;
198
+ },
199
+ throwIfAborted
200
+ };
201
+ };
202
+ //#endregion
203
+ //#region src/chunkedArray.ts
204
+ const getSlicer = (options) => options?.slicer ?? createTimeSlicer({
205
+ budgetMs: options?.budgetMs,
206
+ signal: options?.signal,
207
+ label: options?.label
208
+ });
209
+ /**
210
+ * `Array.prototype.forEach`, chunked: iterates synchronously until the time budget is
211
+ * exhausted, then yields the thread to the host event loop before continuing.
212
+ *
213
+ * Errors thrown by `fn` reject the promise; aborting rejects with an AbortError at the
214
+ * next between-items check point.
215
+ */
216
+ const forEachWithYield = async (items, fn, options) => {
217
+ const slicer = getSlicer(options);
218
+ for (let i = 0; i < items.length; i++) {
219
+ const yielded = slicer.yieldIfNeeded();
220
+ if (yielded) await yielded;
221
+ fn(items[i], i);
222
+ }
223
+ const yielded = slicer.yieldIfNeeded();
224
+ if (yielded) await yielded;
225
+ };
226
+ /** `Array.prototype.map`, chunked (see forEachWithYield for semantics) */
227
+ const mapWithYield = async (items, fn, options) => {
228
+ const results = new Array(items.length);
229
+ await forEachWithYield(items, (item, i) => results[i] = fn(item, i), options);
230
+ return results;
231
+ };
232
+ /** lodash `keyBy`, chunked (see forEachWithYield for semantics) */
233
+ const keyByWithYield = async (items, keyFn, options) => {
234
+ const result = {};
235
+ await forEachWithYield(items, (item, i) => result[keyFn(item, i)] = item, options);
236
+ return result;
237
+ };
238
+ /**
239
+ * Chunked equivalent of lodash `isEqual(a, b)` for arrays: `a === b` fast path, length
240
+ * check, then per-item `a[i] === b[i] || isItemEqual(a[i], b[i])` within the time budget.
241
+ * The result is identical to `isEqual(a, b)`.
242
+ */
243
+ const arrayItemsEqualWithYield = async (a, b, options) => {
244
+ if (a === b) return true;
245
+ if (!a || !b) return false;
246
+ if (a.length !== b.length) return false;
247
+ const isItemEqual = options?.isItemEqual ?? isEqual;
248
+ const slicer = getSlicer(options);
249
+ for (let i = 0; i < a.length; i++) {
250
+ const yielded = slicer.yieldIfNeeded();
251
+ if (yielded) await yielded;
252
+ if (a[i] !== b[i] && !isItemEqual(a[i], b[i])) return false;
253
+ }
254
+ return true;
255
+ };
256
+ //#endregion
257
+ //#region src/deferred.ts
258
+ /**
259
+ * In TypeScript, a deferred promise refers to a pattern that involves creating a promise that can be
260
+ * resolved or rejected at a later point in time, typically by code outside of the current function scope.
261
+ *
262
+ * This pattern is often used when dealing with asynchronous operations that involve multiple steps or when
263
+ * the result of an operation cannot be immediately determined.
264
+ */
34
265
  function Deferred() {
35
- let resolve;
36
- let reject;
37
- let isPending = true;
38
- let isResolved = false;
39
- let isRejected = false;
40
- const promise = new Promise((innerResolve, innerReject) => {
41
- resolve = (value) => {
42
- isPending = false;
43
- isResolved = true;
44
- innerResolve(value);
45
- };
46
- reject = (reason) => {
47
- isPending = false;
48
- isRejected = true;
49
- innerReject(reason);
50
- };
51
- });
52
- return {
53
- promise,
54
- resolve,
55
- reject,
56
- isPending: () => isPending,
57
- isResolved: () => isResolved,
58
- isRejected: () => isRejected
59
- };
266
+ let resolve;
267
+ let reject;
268
+ let isPending = true;
269
+ let isResolved = false;
270
+ let isRejected = false;
271
+ return {
272
+ promise: new Promise((innerResolve, innerReject) => {
273
+ resolve = (value) => {
274
+ isPending = false;
275
+ isResolved = true;
276
+ innerResolve(value);
277
+ };
278
+ reject = (reason) => {
279
+ isPending = false;
280
+ isRejected = true;
281
+ innerReject(reason);
282
+ };
283
+ }),
284
+ resolve,
285
+ reject,
286
+ isPending: () => isPending,
287
+ isResolved: () => isResolved,
288
+ isRejected: () => isRejected
289
+ };
60
290
  }
61
-
62
- // src/firstThenDebounce.ts
63
- import { concat, debounceTime, skip, take } from "rxjs";
64
- var firstThenDebounce = (timeout) => (source) => concat(source.pipe(take(1)), source.pipe(skip(1)).pipe(debounceTime(timeout)));
65
-
66
- // src/formatDecimals.ts
67
- import BigNumber from "bignumber.js";
68
- var MIN_DIGITS = 4;
69
- var MAX_DECIMALS_FORMAT = 12;
70
- var formatDecimals = (num, digits = MIN_DIGITS, options = {}, locale = "en-US") => {
71
- if (num === null || num === void 0) return "";
72
- if (digits < MIN_DIGITS) digits = MIN_DIGITS;
73
- const value = new BigNumber(num);
74
- const minDisplayVal = 1 / 10 ** digits;
75
- if (value.gt(0) && value.lt(minDisplayVal)) return `< ${formatDecimals(minDisplayVal)}`;
76
- const flooredValue = value.integerValue();
77
- const intDigits = flooredValue.isEqualTo(0) ? 0 : flooredValue.toString().length;
78
- let truncatedValue = value;
79
- const excessFractionDigitsPow10 = new BigNumber(10).pow(
80
- digits > intDigits ? digits - intDigits : 0
81
- );
82
- truncatedValue = truncatedValue.multipliedBy(excessFractionDigitsPow10).integerValue().dividedBy(excessFractionDigitsPow10);
83
- const excessIntegerDigits = new BigNumber(intDigits > digits ? intDigits - digits : 0);
84
- const excessIntegerDigitsPow10 = new BigNumber(10).pow(excessIntegerDigits);
85
- if (excessIntegerDigits.gt(0))
86
- truncatedValue = truncatedValue.dividedBy(excessIntegerDigitsPow10).integerValue().multipliedBy(excessIntegerDigitsPow10);
87
- return Intl.NumberFormat(locale, {
88
- // compact notation (K, M, B) if above 9999
89
- notation: truncatedValue.abs().gt(9999) ? "compact" : "standard",
90
- // NOTE: possible values are from `0` to `21`
91
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#maximumsignificantdigits
92
- maximumSignificantDigits: Math.max(1, Math.min(digits + (truncatedValue.lt(1) ? 1 : 0), 21)),
93
- ...options
94
- }).format(truncatedValue.toNumber());
291
+ //#endregion
292
+ //#region src/firstThenDebounce.ts
293
+ /**
294
+ * An rxjs operator which:
295
+ *
296
+ * 1. Emits the first value it receives from the source observable, then:
297
+ * 2. Debounces any future values by `timeout` ms.
298
+ */
299
+ const firstThenDebounce = (timeout) => (source) => concat(source.pipe(take(1)), source.pipe(skip(1)).pipe(debounceTime(timeout)));
300
+ //#endregion
301
+ //#region src/formatDecimals.ts
302
+ const MIN_DIGITS = 4;
303
+ const MAX_DECIMALS_FORMAT = 12;
304
+ /**
305
+ * Custom decimal number formatting for Talisman
306
+ * note that the NumberFormat().format() call is the ressource heavy part, it's not worth trying to optimize other parts
307
+ * @param num input number
308
+ * @param digits number of significant digits to display
309
+ * @param locale locale used to format the number
310
+ * @param options formatting options
311
+ * @returns the formatted value
312
+ */
313
+ const formatDecimals = (num, digits = MIN_DIGITS, options = {}, locale = "en-US") => {
314
+ if (num === null || num === void 0) return "";
315
+ if (digits < MIN_DIGITS) digits = MIN_DIGITS;
316
+ const value = new configureBigNumber_default(num);
317
+ const minDisplayVal = 1 / 10 ** digits;
318
+ if (value.gt(0) && value.lt(minDisplayVal)) return `< ${formatDecimals(minDisplayVal)}`;
319
+ const flooredValue = value.integerValue();
320
+ const intDigits = flooredValue.isEqualTo(0) ? 0 : flooredValue.toString().length;
321
+ let truncatedValue = value;
322
+ const excessFractionDigitsPow10 = new configureBigNumber_default(10).pow(digits > intDigits ? digits - intDigits : 0);
323
+ truncatedValue = truncatedValue.multipliedBy(excessFractionDigitsPow10).integerValue().dividedBy(excessFractionDigitsPow10);
324
+ const excessIntegerDigits = new configureBigNumber_default(intDigits > digits ? intDigits - digits : 0);
325
+ const excessIntegerDigitsPow10 = new configureBigNumber_default(10).pow(excessIntegerDigits);
326
+ if (excessIntegerDigits.gt(0)) truncatedValue = truncatedValue.dividedBy(excessIntegerDigitsPow10).integerValue().multipliedBy(excessIntegerDigitsPow10);
327
+ return Intl.NumberFormat(locale, {
328
+ notation: truncatedValue.abs().gt(9999) ? "compact" : "standard",
329
+ maximumSignificantDigits: Math.max(1, Math.min(digits + (truncatedValue.lt(1) ? 1 : 0), 21)),
330
+ ...options
331
+ }).format(truncatedValue.toNumber());
95
332
  };
96
-
97
- // src/formatPrice.ts
98
- var formatPrice = (price, currency, compact) => {
99
- return Intl.NumberFormat(void 0, {
100
- style: "currency",
101
- currency,
102
- currencyDisplay: currency === "usd" ? "narrowSymbol" : "symbol",
103
- minimumSignificantDigits: 3,
104
- maximumSignificantDigits: compact ? price < 1 ? 3 : 4 : void 0,
105
- roundingPriority: compact ? "auto" : "morePrecision",
106
- notation: compact ? "compact" : "standard"
107
- }).format(price);
333
+ //#endregion
334
+ //#region src/formatPrice.ts
335
+ const formatPrice = (price, currency, compact) => {
336
+ return Intl.NumberFormat(void 0, {
337
+ style: "currency",
338
+ currency,
339
+ currencyDisplay: currency === "usd" ? "narrowSymbol" : "symbol",
340
+ minimumSignificantDigits: 3,
341
+ maximumSignificantDigits: compact ? price < 1 ? 3 : 4 : void 0,
342
+ roundingPriority: compact ? "auto" : "morePrecision",
343
+ notation: compact ? "compact" : "standard"
344
+ }).format(price);
108
345
  };
109
-
110
- // src/getLoadable.ts
111
- import { catchError, from, map, of, startWith, switchMap, timer } from "rxjs";
346
+ //#endregion
347
+ //#region src/getLoadable.ts
112
348
  function getLoadable$(factory, options = {}) {
113
- const { getError, refreshInterval } = options;
114
- const createLoadableStream = () => from(factory()).pipe(
115
- map(
116
- (data) => ({
117
- status: "success",
118
- data
119
- })
120
- ),
121
- catchError(
122
- (error) => of({
123
- status: "error",
124
- error: getError ? getError(error) : getGenericError(error)
125
- })
126
- )
127
- );
128
- const source$ = refreshInterval ? timer(0, refreshInterval).pipe(switchMap(() => createLoadableStream())) : createLoadableStream();
129
- return source$.pipe(
130
- startWith({
131
- status: "loading"
132
- })
133
- );
349
+ const { getError, refreshInterval } = options;
350
+ const createLoadableStream = () => from(factory()).pipe(map((data) => ({
351
+ status: "success",
352
+ data
353
+ })), catchError((error) => of({
354
+ status: "error",
355
+ error: getError ? getError(error) : getGenericError(error)
356
+ })));
357
+ return (refreshInterval ? timer(0, refreshInterval).pipe(switchMap(() => createLoadableStream())) : createLoadableStream()).pipe(startWith({ status: "loading" }));
134
358
  }
135
- var getGenericError = (error) => ({
136
- name: "Error",
137
- message: getGenericErrorMessage(error)
359
+ const getGenericError = (error) => ({
360
+ name: "Error",
361
+ message: getGenericErrorMessage(error)
138
362
  });
139
- var getGenericErrorMessage = (error) => {
140
- if (typeof error === "string") {
141
- return error;
142
- } else if (error instanceof Error) {
143
- return error.message;
144
- } else if (error && typeof error === "object" && "message" in error) {
145
- return error.message;
146
- }
147
- return String(error) || "Unknown error";
363
+ const getGenericErrorMessage = (error) => {
364
+ if (typeof error === "string") return error;
365
+ else if (error instanceof Error) return error.message;
366
+ else if (error && typeof error === "object" && "message" in error) return error.message;
367
+ return String(error) || "Unknown error";
148
368
  };
149
-
150
- // src/getLoadableQuery.ts
151
- import { map as map2, startWith as startWith2 } from "rxjs";
152
-
153
- // src/getQuery.ts
154
- import { isEqual } from "lodash-es";
155
- import { BehaviorSubject, distinctUntilChanged, Observable, shareReplay as shareReplay2 } from "rxjs";
156
-
157
- // src/getSharedObservable.ts
158
- import { shareReplay } from "rxjs";
159
- var CACHE = /* @__PURE__ */ new Map();
160
- var getSharedObservable = (namespace, args, createObservable, serializer = (args2) => JSON.stringify(args2)) => {
161
- const cacheKey = `${namespace}:${serializer(args)}`;
162
- if (CACHE.has(cacheKey)) return CACHE.get(cacheKey);
163
- const obs = createObservable(args);
164
- const sharedObs = obs.pipe(shareReplay({ bufferSize: 1, refCount: true }));
165
- CACHE.set(cacheKey, sharedObs);
166
- return sharedObs;
369
+ //#endregion
370
+ //#region src/getSharedObservable.ts
371
+ const CACHE = /* @__PURE__ */ new Map();
372
+ /**
373
+ * How long an entry survives with no subscribers before being dropped. Long enough
374
+ * for quick unmount/remount cycles (navigation, react-rxjs Subscribe boundaries) to
375
+ * reuse the entry, short enough that abandoned scopes (e.g. superseded balance
376
+ * subscriptions after networks/accounts change) don't accumulate forever.
377
+ */
378
+ const CLEANUP_DELAY_MS = 6e4;
379
+ /**
380
+ * When using react-rxjs hooks and state observables, the options are used as weak map keys.
381
+ * This means that if the options object is recreated on each render, the observable will be recreated as well.
382
+ * This utility function allows you to create a shared observable based on a namespace and arguments that, so react-rxjs can reuse the same observables
383
+ *
384
+ * Entries are reference-counted: an entry with no subscribers for CLEANUP_DELAY_MS is
385
+ * removed from the cache (its shareReplay source is already torn down by refCount at
386
+ * that point — only the replay buffer and the entry itself are dropped).
387
+ *
388
+ * @param namespace
389
+ * @param args
390
+ * @param createObservable
391
+ * @param serializer
392
+ * @returns
393
+ */
394
+ const getSharedObservable = (namespace, args, createObservable, serializer = (args) => JSON.stringify(args)) => {
395
+ const cacheKey = `${namespace}:${serializer(args)}`;
396
+ const cached = CACHE.get(cacheKey);
397
+ if (cached) return cached.observable;
398
+ const sharedObs = createObservable(args).pipe(shareReplay({
399
+ bufferSize: 1,
400
+ refCount: true
401
+ }));
402
+ const scheduleCleanup = (entry) => {
403
+ if (entry.cleanupTimer) clearTimeout(entry.cleanupTimer);
404
+ entry.cleanupTimer = setTimeout(() => {
405
+ if (entry.refCount === 0 && CACHE.get(cacheKey) === entry) CACHE.delete(cacheKey);
406
+ }, CLEANUP_DELAY_MS);
407
+ };
408
+ const entry = {
409
+ observable: null,
410
+ refCount: 0,
411
+ cleanupTimer: null
412
+ };
413
+ entry.observable = new Observable((subscriber) => {
414
+ entry.refCount++;
415
+ if (entry.cleanupTimer) {
416
+ clearTimeout(entry.cleanupTimer);
417
+ entry.cleanupTimer = null;
418
+ }
419
+ const subscription = sharedObs.subscribe(subscriber);
420
+ return () => {
421
+ subscription.unsubscribe();
422
+ entry.refCount--;
423
+ if (entry.refCount === 0) scheduleCleanup(entry);
424
+ };
425
+ });
426
+ CACHE.set(cacheKey, entry);
427
+ scheduleCleanup(entry);
428
+ return entry.observable;
167
429
  };
168
-
169
- // src/getQuery.ts
170
- var getQuery$ = ({
171
- namespace,
172
- args,
173
- queryFn,
174
- defaultValue,
175
- refreshInterval,
176
- serializer = (args2) => JSON.stringify(args2)
177
- }) => {
178
- return getSharedObservable(
179
- namespace,
180
- args,
181
- () => new Observable((subscriber) => {
182
- const controller = new AbortController();
183
- const result = new BehaviorSubject({
184
- status: "loading",
185
- data: defaultValue,
186
- error: void 0
187
- });
188
- const sub = result.pipe(distinctUntilChanged(isEqual)).subscribe(subscriber);
189
- let timeout = null;
190
- const run = () => {
191
- if (controller.signal.aborted) return;
192
- queryFn(args, controller.signal).then((data) => {
193
- if (controller.signal.aborted) return;
194
- result.next({ status: "loaded", data, error: void 0 });
195
- }).catch((error) => {
196
- if (controller.signal.aborted) return;
197
- result.next({ status: "error", data: void 0, error });
198
- }).finally(() => {
199
- if (controller.signal.aborted) return;
200
- if (refreshInterval) timeout = setTimeout(run, refreshInterval);
201
- });
202
- };
203
- run();
204
- return () => {
205
- sub.unsubscribe();
206
- if (timeout) clearTimeout(timeout);
207
- controller.abort(new Error("getQuery$ unsubscribed"));
208
- };
209
- }).pipe(shareReplay2({ refCount: true, bufferSize: 1 })),
210
- serializer
211
- );
430
+ //#endregion
431
+ //#region src/getQuery.ts
432
+ /**
433
+ * Creates a shared observable for executing queries with caching, loading states, and automatic refresh capabilities.
434
+ *
435
+ * @example
436
+ * ```typescript
437
+ * const userQuery$ = getQuery$({
438
+ * namespace: 'users',
439
+ * args: { userId: 123 },
440
+ * queryFn: async ({ userId }) => fetchUser(userId),
441
+ * defaultValue: null,
442
+ * refreshInterval: 30000
443
+ * });
444
+ *
445
+ * userQuery$.subscribe(result => {
446
+ * if (result.status === 'loaded') {
447
+ * console.log(result.data);
448
+ * }
449
+ * });
450
+ * ```
451
+ *
452
+ * @deprecated use getLoadableQuery$ instead
453
+ */
454
+ const getQuery$ = ({ namespace, args, queryFn, defaultValue, refreshInterval, serializer = (args) => JSON.stringify(args) }) => {
455
+ return getSharedObservable(namespace, args, () => new Observable((subscriber) => {
456
+ const controller = new AbortController();
457
+ const result = new BehaviorSubject({
458
+ status: "loading",
459
+ data: defaultValue,
460
+ error: void 0
461
+ });
462
+ const sub = result.pipe(distinctUntilChanged(isEqual)).subscribe(subscriber);
463
+ let timeout = null;
464
+ const run = () => {
465
+ if (controller.signal.aborted) return;
466
+ queryFn(args, controller.signal).then((data) => {
467
+ if (controller.signal.aborted) return;
468
+ result.next({
469
+ status: "loaded",
470
+ data,
471
+ error: void 0
472
+ });
473
+ }).catch((error) => {
474
+ if (controller.signal.aborted) return;
475
+ result.next({
476
+ status: "error",
477
+ data: void 0,
478
+ error
479
+ });
480
+ }).finally(() => {
481
+ if (controller.signal.aborted) return;
482
+ if (refreshInterval) timeout = setTimeout(run, refreshInterval);
483
+ });
484
+ };
485
+ run();
486
+ return () => {
487
+ sub.unsubscribe();
488
+ if (timeout) clearTimeout(timeout);
489
+ controller.abort(/* @__PURE__ */ new Error("getQuery$ unsubscribed"));
490
+ };
491
+ }).pipe(shareReplay({
492
+ refCount: true,
493
+ bufferSize: 1
494
+ })), serializer);
212
495
  };
213
-
214
- // src/getLoadableQuery.ts
215
- var getLoadableQuery$ = (params) => {
216
- const initial = params.defaultValue === void 0 ? [] : [{ status: "loading", data: params.defaultValue }];
217
- return getQuery$(params).pipe(
218
- map2((val) => {
219
- switch (val.status) {
220
- case "loading":
221
- return { status: "loading", data: val.data };
222
- case "loaded":
223
- return { status: "success", data: val.data };
224
- case "error": {
225
- const err = val.error;
226
- return {
227
- status: "error",
228
- error: {
229
- name: err?.name ?? "QueryError",
230
- message: err?.message ?? "Failed to execute query"
231
- }
232
- };
233
- }
234
- }
235
- }),
236
- startWith2(...initial)
237
- );
496
+ //#endregion
497
+ //#region src/getLoadableQuery.ts
498
+ /**
499
+ * Thin wrapper around getQuery$ that returns Loadable<T> and optionally
500
+ * primes the stream with a loading state using the provided default value.
501
+ *
502
+ * TODO: consolidate with getQuery$
503
+ */
504
+ const getLoadableQuery$ = (params) => {
505
+ const initial = params.defaultValue === void 0 ? [] : [{
506
+ status: "loading",
507
+ data: params.defaultValue
508
+ }];
509
+ return getQuery$(params).pipe(map((val) => {
510
+ switch (val.status) {
511
+ case "loading": return {
512
+ status: "loading",
513
+ data: val.data
514
+ };
515
+ case "loaded": return {
516
+ status: "success",
517
+ data: val.data
518
+ };
519
+ case "error": {
520
+ const err = val.error;
521
+ return {
522
+ status: "error",
523
+ error: {
524
+ name: err?.name ?? "QueryError",
525
+ message: err?.message ?? "Failed to execute query"
526
+ }
527
+ };
528
+ }
529
+ }
530
+ }), startWith(...initial));
238
531
  };
239
-
240
- // src/hasOwnProperty.ts
532
+ //#endregion
533
+ //#region src/hasOwnProperty.ts
241
534
  function hasOwnProperty(obj, prop) {
242
- if (typeof obj !== "object") return false;
243
- if (obj === null) return false;
244
- return prop in obj;
535
+ if (typeof obj !== "object") return false;
536
+ if (obj === null) return false;
537
+ return prop in obj;
245
538
  }
246
-
247
- // src/isAbortError.ts
248
- var isAbortError = (error) => {
249
- return error instanceof Error && error.name === "AbortError";
539
+ //#endregion
540
+ //#region src/isAbortError.ts
541
+ const isAbortError = (error) => {
542
+ return error instanceof Error && error.name === "AbortError";
250
543
  };
251
-
252
- // src/isArrayOf.ts
544
+ //#endregion
545
+ //#region src/isArrayOf.ts
253
546
  function isArrayOf(array, func) {
254
- if (array.length > 0 && array[0] instanceof func) return true;
255
- return false;
547
+ if (array.length > 0 && array[0] instanceof func) return true;
548
+ return false;
256
549
  }
257
-
258
- // src/isAscii.ts
259
- var isAscii = (str) => {
260
- return [...str].every((char) => char.charCodeAt(0) <= 127);
550
+ //#endregion
551
+ //#region src/isAscii.ts
552
+ const isAscii = (str) => {
553
+ return [...str].every((char) => char.charCodeAt(0) <= 127);
261
554
  };
262
-
263
- // src/isBigInt.ts
264
- var isBigInt = (value) => typeof value === "bigint";
265
-
266
- // src/isBooleanTrue.ts
267
- var isBooleanTrue = (x) => !!x;
268
-
269
- // src/isHexString.ts
270
- var REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
271
- var isHexString = (value) => {
272
- return typeof value === "string" && REGEX_HEX_STRING.test(value);
273
- };
274
-
275
- // src/isNotNil.ts
276
- var isNotNil = (value) => value !== null && value !== void 0;
277
-
278
- // src/isPromise.ts
279
- var isPromise = (value) => !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
280
-
281
- // src/isSubject.ts
282
- import { Subject } from "rxjs";
555
+ //#endregion
556
+ //#region src/isBigInt.ts
557
+ const isBigInt = (value) => typeof value === "bigint";
558
+ //#endregion
559
+ //#region src/isBooleanTrue.ts
560
+ const isBooleanTrue = (x) => !!x;
561
+ //#endregion
562
+ //#region src/isErrorOfName.ts
563
+ /**
564
+ * Robust replacement for `error instanceof SomeErrorClass` when the class comes from a
565
+ * package that gets bundled into multiple chunks/contexts (e.g. viem, @solana/web3.js).
566
+ *
567
+ * `instanceof` returns `false` whenever the error originates from a different bundled copy of
568
+ * the module than the imported class reference — which silently broke Solana transfers (see
569
+ * apps/extension/src/__tests__/no-instanceof-bundled-class.test.ts). An error's `.name` is
570
+ * stable across copies, *provided the class sets it explicitly* (viem does:
571
+ * `super({ name: 'ContractFunctionExecutionError' })`). For classes that don't set `.name`,
572
+ * duck-type the shape instead.
573
+ */
574
+ const isErrorOfName = (error, ...names) => error instanceof Error && names.includes(error.name);
575
+ //#endregion
576
+ //#region src/isNotNil.ts
577
+ /**
578
+ * WARNING: This function only checks against null or undefined, it does not coerce the value.
579
+ * ie: false and 0 are considered not nil
580
+ * Use isTruthy instead for a regular coercion check.
581
+ *
582
+ * @param value
583
+ * @returns whether the value is neither null nor undefined
584
+ */
585
+ const isNotNil = (value) => value !== null && value !== void 0;
586
+ //#endregion
587
+ //#region src/isPromise.ts
588
+ const isPromise = (value) => !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
589
+ //#endregion
590
+ //#region src/isSubject.ts
591
+ /**
592
+ * Tests to see if an object is an RxJS {@link Subject}.
593
+ */
283
594
  function isSubject(object) {
284
- if (!object) return false;
285
- if (object instanceof Subject) return true;
286
- return "asObservable" in object && isFn(object.asObservable) && "complete" in object && isFn(object.complete) && "error" in object && isFn(object.error) && "forEach" in object && isFn(object.forEach) && "next" in object && isFn(object.next) && "pipe" in object && isFn(object.pipe) && "subscribe" in object && isFn(object.subscribe) && "unsubscribe" in object && isFn(object.unsubscribe) && "closed" in object && isBool(object.closed) && "observed" in object && isBool(object.observed);
595
+ if (!object) return false;
596
+ if (object instanceof Subject) return true;
597
+ return "asObservable" in object && isFn(object.asObservable) && "complete" in object && isFn(object.complete) && "error" in object && isFn(object.error) && "forEach" in object && isFn(object.forEach) && "next" in object && isFn(object.next) && "pipe" in object && isFn(object.pipe) && "subscribe" in object && isFn(object.subscribe) && "unsubscribe" in object && isFn(object.unsubscribe) && "closed" in object && isBool(object.closed) && "observed" in object && isBool(object.observed);
287
598
  }
599
+ /**
600
+ * Returns `true` if `value` is a function.
601
+ */
288
602
  function isFn(value) {
289
- return typeof value === "function";
603
+ return typeof value === "function";
290
604
  }
605
+ /**
606
+ * Returns `true` if `value` is a boolean.
607
+ */
291
608
  function isBool(value) {
292
- return typeof value === "boolean";
609
+ return typeof value === "boolean";
293
610
  }
294
-
295
- // src/isTruthy.ts
296
- var isTruthy = (value) => Boolean(value);
297
-
298
- // src/keepAlive.ts
299
- import { shareReplay as shareReplay3, tap } from "rxjs";
300
- var keepAlive = (timeout) => {
301
- let release;
302
- return (source) => source.pipe(
303
- tap({
304
- subscribe: () => {
305
- release = keepSourceSubscribed(source, timeout);
306
- },
307
- unsubscribe: () => {
308
- release?.();
309
- }
310
- }),
311
- shareReplay3({ refCount: true, bufferSize: 1 })
312
- );
313
- };
314
- var keepSourceSubscribed = (observable, ms) => {
315
- const sub = observable.subscribe();
316
- return () => setTimeout(() => sub.unsubscribe(), ms);
611
+ //#endregion
612
+ //#region src/isTruthy.ts
613
+ const isTruthy = (value) => Boolean(value);
614
+ //#endregion
615
+ //#region src/keepAlive.ts
616
+ /**
617
+ * An RxJS operator that keeps the source observable alive for a specified duration
618
+ * after all subscribers have unsubscribed. This prevents expensive re-subscriptions
619
+ * when subscribers come and go frequently.
620
+ *
621
+ * @param keepAliveMs - Duration in milliseconds to keep the source alive after last unsubscription
622
+ * @returns MonoTypeOperatorFunction that can be used in pipe()
623
+ *
624
+ * @example
625
+ * ```typescript
626
+ * const data$ = expensive_api_call$.pipe(
627
+ * keepAlive(3000) // Keep alive for 3 seconds
628
+ * );
629
+ * ```
630
+ */
631
+ const keepAlive = (timeout) => {
632
+ let release;
633
+ return (source) => source.pipe(tap({
634
+ subscribe: () => {
635
+ release = keepSourceSubscribed(source, timeout);
636
+ },
637
+ unsubscribe: () => {
638
+ release?.();
639
+ }
640
+ }), shareReplay({
641
+ refCount: true,
642
+ bufferSize: 1
643
+ }));
317
644
  };
318
-
319
- // src/planckToTokens.ts
320
- import BigNumber2 from "bignumber.js";
645
+ const keepSourceSubscribed = (observable, ms) => {
646
+ const sub = observable.subscribe();
647
+ return () => setTimeout(() => sub.unsubscribe(), ms);
648
+ };
649
+ //#endregion
650
+ //#region src/lruMap.ts
651
+ /**
652
+ * A Map with a maximum size and least-recently-used eviction.
653
+ *
654
+ * Reads refresh recency; when an insert would exceed `maxSize`, the
655
+ * least-recently-used entry is evicted. Intended for long-lived in-memory
656
+ * caches that would otherwise grow without bound.
657
+ */
658
+ var LruMap = class {
659
+ #map = /* @__PURE__ */ new Map();
660
+ #maxSize;
661
+ constructor(maxSize) {
662
+ if (maxSize < 1) throw new Error("LruMap maxSize must be >= 1");
663
+ this.#maxSize = maxSize;
664
+ }
665
+ get(key) {
666
+ if (!this.#map.has(key)) return void 0;
667
+ const value = this.#map.get(key);
668
+ this.#map.delete(key);
669
+ this.#map.set(key, value);
670
+ return value;
671
+ }
672
+ set(key, value) {
673
+ if (this.#map.has(key)) this.#map.delete(key);
674
+ else if (this.#map.size >= this.#maxSize) {
675
+ const oldest = this.#map.keys().next();
676
+ if (!oldest.done) this.#map.delete(oldest.value);
677
+ }
678
+ this.#map.set(key, value);
679
+ return this;
680
+ }
681
+ has(key) {
682
+ return this.#map.has(key);
683
+ }
684
+ delete(key) {
685
+ return this.#map.delete(key);
686
+ }
687
+ clear() {
688
+ this.#map.clear();
689
+ }
690
+ get size() {
691
+ return this.#map.size;
692
+ }
693
+ };
694
+ //#endregion
695
+ //#region src/mapChunked.ts
696
+ const runChunkedProject = (project, options, value, index) => new Observable((subscriber) => {
697
+ const controller = new AbortController();
698
+ project(value, {
699
+ slicer: createTimeSlicer({
700
+ budgetMs: options?.budgetMs,
701
+ signal: controller.signal,
702
+ label: options?.label
703
+ }),
704
+ signal: controller.signal
705
+ }, index).then((result) => {
706
+ if (!controller.signal.aborted) subscriber.next(result);
707
+ subscriber.complete();
708
+ }, (error) => {
709
+ if (isAbortError(error)) subscriber.complete();
710
+ else subscriber.error(error);
711
+ });
712
+ return () => controller.abort();
713
+ });
714
+ /**
715
+ * `switchMap` for time-sliced (chunked) async work — latest-wins: a new upstream value or
716
+ * an unsubscription aborts the in-flight `project` via `ctx.signal` / the shared slicer,
717
+ * so cancelled work stops burning CPU at its next yield check point and its result is
718
+ * never emitted.
719
+ *
720
+ * Non-abort errors thrown by `project` error the stream (same as a throwing sync `map`).
721
+ *
722
+ * Note: pipelines using this operator already emit asynchronously (macrotask), so adding
723
+ * `observeOn(asyncScheduler)` downstream is redundant and only adds latency.
724
+ */
725
+ const switchMapChunked = (project, options) => switchMap((value, index) => runChunkedProject(project, options, value, index));
726
+ /**
727
+ * `concatMap` variant of switchMapChunked — ordered: upstream values queue and are
728
+ * processed one at a time; a new value does NOT cancel in-flight work (unsubscription
729
+ * still aborts it).
730
+ */
731
+ const concatMapChunked = (project, options) => concatMap((value, index) => runChunkedProject(project, options, value, index));
732
+ //#endregion
733
+ //#region src/planckToTokens.ts
321
734
  function planckToTokens(planck, tokenDecimals) {
322
- if (typeof planck !== "string" || typeof tokenDecimals !== "number") return;
323
- const base = 10;
324
- const exponent = -1 * tokenDecimals;
325
- const multiplier = base ** exponent;
326
- return new BigNumber2(planck).multipliedBy(multiplier).toString(10);
735
+ if (typeof planck !== "string" || typeof tokenDecimals !== "number") return;
736
+ const multiplier = 10 ** (-1 * tokenDecimals);
737
+ return new configureBigNumber_default(planck).multipliedBy(multiplier).toString(10);
327
738
  }
328
-
329
- // src/replaySubjectFrom.ts
330
- import { ReplaySubject } from "rxjs";
331
- var replaySubjectFrom = (initialValue) => {
332
- if (initialValue instanceof ReplaySubject) return initialValue;
333
- const subject = new ReplaySubject(1);
334
- if (isPromise(initialValue)) {
335
- initialValue.then(
336
- (value) => subject.next(value),
337
- (error) => subject.error(error)
338
- );
339
- return subject;
340
- }
341
- subject.next(initialValue);
342
- return subject;
739
+ //#endregion
740
+ //#region src/replaySubjectFrom.ts
741
+ /**
742
+ * Turns a value into a {@link ReplaySubject} of size 1.
743
+ *
744
+ * If the value is already a {@link ReplaySubject}, it will be returned as-is.
745
+ *
746
+ * If the value is a {@link Promise}, it will be awaited,
747
+ * and the awaited value will be published into the {@link ReplaySubject} when it becomes available.
748
+ *
749
+ * For any other type of value, it will be immediately published into the {@link ReplaySubject}.
750
+ */
751
+ const replaySubjectFrom = (initialValue) => {
752
+ if (initialValue instanceof ReplaySubject) return initialValue;
753
+ const subject = new ReplaySubject(1);
754
+ if (isPromise(initialValue)) {
755
+ initialValue.then((value) => subject.next(value), (error) => subject.error(error));
756
+ return subject;
757
+ }
758
+ subject.next(initialValue);
759
+ return subject;
343
760
  };
344
-
345
- // src/sleep.ts
346
- var sleep = (ms) => new Promise((resolve) => {
347
- if (process.env.NODE_ENV === "test") resolve();
348
- else setTimeout(resolve, ms);
761
+ //#endregion
762
+ //#region src/sleep.ts
763
+ const sleep = (ms) => new Promise((resolve) => {
764
+ if (process.env.NODE_ENV === "test") resolve();
765
+ else setTimeout(resolve, ms);
349
766
  });
350
-
351
- // src/splitSubject.ts
767
+ //#endregion
768
+ //#region src/splitSubject.ts
769
+ /**
770
+ * Takes a subject and splits it into two parts:
771
+ *
772
+ * 1. A function to submit new values into the subject.
773
+ * 2. An observable for subscribing to new values from the subject.
774
+ *
775
+ * This can be helpful when, to avoid bugs, you want to expose only one
776
+ * of these parts to external code and keep the other part private.
777
+ */
352
778
  function splitSubject(subject) {
353
- const next = (value) => subject.next(value);
354
- const observable = subject.asObservable();
355
- return [next, observable];
779
+ const next = (value) => subject.next(value);
780
+ return [next, subject.asObservable()];
356
781
  }
357
-
358
- // src/throwAfter.ts
359
- var throwAfter = (ms, reason) => new Promise((_, reject) => setTimeout(() => reject(new Error(reason)), ms));
360
-
361
- // src/tokensToPlanck.ts
362
- import BigNumber3 from "bignumber.js";
782
+ //#endregion
783
+ //#region src/stripHexPrefix.ts
784
+ /**
785
+ * @name stripHexPrefix
786
+ * @description Removes a leading `0x` prefix from a string, if present.
787
+ * @param {string} str - string to strip
788
+ * @returns {string} - the string without its leading `0x` prefix
789
+ * @example
790
+ * stripHexPrefix("0x1234") // "1234"
791
+ * stripHexPrefix("1234") // "1234"
792
+ **/
793
+ const stripHexPrefix = (str) => str.startsWith("0x") ? str.slice(2) : str;
794
+ //#endregion
795
+ //#region src/throwAfter.ts
796
+ const throwAfter = (ms, reason) => new Promise((_, reject) => setTimeout(() => reject(new Error(reason)), ms));
797
+ //#endregion
798
+ //#region src/tokensToPlanck.ts
363
799
  function tokensToPlanck(tokens, tokenDecimals) {
364
- if (typeof tokens !== "string" || typeof tokenDecimals !== "number") return;
365
- const base = 10;
366
- const exponent = tokenDecimals;
367
- const multiplier = base ** exponent;
368
- return new BigNumber3(tokens).multipliedBy(multiplier).toString(10);
800
+ if (typeof tokens !== "string" || typeof tokenDecimals !== "number") return;
801
+ const multiplier = 10 ** tokenDecimals;
802
+ return new configureBigNumber_default(tokens).multipliedBy(multiplier).toString(10);
369
803
  }
370
-
371
- // src/validateHexString.ts
372
- var validateHexString = (str) => {
373
- if (typeof str !== "string") {
374
- throw new Error("Expected a string");
375
- }
376
- if (str.startsWith("0x")) {
377
- return str;
378
- }
379
- throw new Error("Expected a hex string");
380
- };
381
- export {
382
- BigMath,
383
- Deferred,
384
- MAX_DECIMALS_FORMAT,
385
- REGEX_HEX_STRING,
386
- addTrailingSlash,
387
- firstThenDebounce,
388
- formatDecimals,
389
- formatPrice,
390
- getLoadable$,
391
- getLoadableQuery$,
392
- getQuery$,
393
- getSharedObservable,
394
- hasOwnProperty,
395
- isAbortError,
396
- isArrayOf,
397
- isAscii,
398
- isBigInt,
399
- isBooleanTrue,
400
- isHexString,
401
- isNotNil,
402
- isPromise,
403
- isSubject,
404
- isTruthy,
405
- keepAlive,
406
- planckToTokens,
407
- replaySubjectFrom,
408
- sleep,
409
- splitSubject,
410
- throwAfter,
411
- tokensToPlanck,
412
- validateHexString
804
+ //#endregion
805
+ //#region src/validateArrayWithYield.ts
806
+ /**
807
+ * Validates every item of an array within a cooperative time budget, yielding the thread
808
+ * to the host event loop whenever the budget for the current slice is exhausted.
809
+ *
810
+ * Like `z.array(schema).safeParse`, ALL items are validated and all failures collected
811
+ * (no fail-fast) — pass e.g. `(item) => SomeSchema.safeParse(item)` as `parseItem`.
812
+ * Zod-agnostic on purpose: this package has no zod dependency.
813
+ */
814
+ const validateArrayWithYield = async (items, parseItem, options) => {
815
+ const data = new Array(items.length);
816
+ const errors = [];
817
+ await forEachWithYield(items, (item, index) => {
818
+ const result = parseItem(item, index);
819
+ if (result.success) data[index] = result.data;
820
+ else errors.push({
821
+ index,
822
+ error: result.error
823
+ });
824
+ }, options);
825
+ return errors.length ? {
826
+ success: false,
827
+ errors
828
+ } : {
829
+ success: true,
830
+ data
831
+ };
832
+ };
833
+ //#endregion
834
+ //#region src/validateHexString.ts
835
+ /**
836
+ * @name validateHexString
837
+ * @description Checks if a string is a hex string. Required to account for type differences between different polkadot libraries
838
+ * @param {string} str - string to check
839
+ * @returns {`0x${string}`} - boolean
840
+ * @example
841
+ * validateHexString("0x1234") // "0x1234"
842
+ * validateHexString("1234") // Error: Expected a hex string
843
+ * validateHexString(1234) // Error: Expected a string
844
+ **/
845
+ const validateHexString = (str) => {
846
+ if (typeof str !== "string") throw new Error("Expected a string");
847
+ if (str.startsWith("0x")) return str;
848
+ throw new Error("Expected a hex string");
413
849
  };
850
+ //#endregion
851
+ export { BigMath, DEFAULT_TIME_SLICE_BUDGET_MS, Deferred, LruMap, MAX_DECIMALS_FORMAT, REGEX_HEX_STRING, addTrailingSlash, arrayItemsEqualWithYield, assert, concatMapChunked, createTimeSlicer, firstThenDebounce, forEachWithYield, formatDecimals, formatPrice, getLoadable$, getLoadableQuery$, getQuery$, getSharedObservable, hasOwnProperty, hexToNumber, hexToString, hexToU8a, isAbortError, isArrayOf, isAscii, isAsciiPrintable, isBigInt, isBooleanTrue, isErrorOfName, isHexString, isNotNil, isPromise, isSubject, isTruthy, keepAlive, keyByWithYield, mapWithYield, newAbortError, numberToU8a, planckToTokens, replaySubjectFrom, reportJsActivity, sleep, splitSubject, stringToU8a, stripHexPrefix, switchMapChunked, throwAfter, tokensToPlanck, u8aCmp, u8aConcat, u8aEq, u8aToHex, u8aToString, u8aToU8a, u8aUnwrapBytes, u8aWrapBytes, validateArrayWithYield, validateHexString, yieldToEventLoop };
852
+
414
853
  //# sourceMappingURL=index.mjs.map