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