@talismn/util 0.5.6 → 0.5.8
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.d.mts +270 -0
- package/dist/index.d.ts +270 -0
- package/dist/index.js +490 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +421 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +23 -19
- package/dist/declarations/src/BigMath.d.ts +0 -10
- package/dist/declarations/src/FunctionPropertyNames.d.ts +0 -8
- package/dist/declarations/src/Prettify.d.ts +0 -3
- package/dist/declarations/src/addTrailingSlash.d.ts +0 -1
- package/dist/declarations/src/classNames.d.ts +0 -2
- package/dist/declarations/src/deferred.d.ts +0 -15
- package/dist/declarations/src/firstThenDebounce.d.ts +0 -8
- package/dist/declarations/src/formatDecimals.d.ts +0 -12
- package/dist/declarations/src/formatPrice.d.ts +0 -1
- package/dist/declarations/src/getLoadable.d.ts +0 -25
- package/dist/declarations/src/getQuery.d.ts +0 -45
- package/dist/declarations/src/getSharedObservable.d.ts +0 -13
- package/dist/declarations/src/hasOwnProperty.d.ts +0 -1
- package/dist/declarations/src/index.d.ts +0 -31
- package/dist/declarations/src/isAbortError.d.ts +0 -1
- package/dist/declarations/src/isArrayOf.d.ts +0 -1
- package/dist/declarations/src/isAscii.d.ts +0 -1
- package/dist/declarations/src/isBigInt.d.ts +0 -1
- package/dist/declarations/src/isBooleanTrue.d.ts +0 -1
- package/dist/declarations/src/isHexString.d.ts +0 -3
- package/dist/declarations/src/isNotNil.d.ts +0 -9
- package/dist/declarations/src/isPromise.d.ts +0 -1
- package/dist/declarations/src/isSubject.d.ts +0 -5
- package/dist/declarations/src/isTruthy.d.ts +0 -1
- package/dist/declarations/src/keepAlive.d.ts +0 -17
- package/dist/declarations/src/planckToTokens.d.ts +0 -3
- package/dist/declarations/src/replaySubjectFrom.d.ts +0 -12
- package/dist/declarations/src/sleep.d.ts +0 -1
- package/dist/declarations/src/splitSubject.d.ts +0 -11
- package/dist/declarations/src/throwAfter.d.ts +0 -1
- package/dist/declarations/src/tokensToPlanck.d.ts +0 -3
- package/dist/declarations/src/validateHexString.d.ts +0 -11
- package/dist/talismn-util.cjs.d.ts +0 -1
- package/dist/talismn-util.cjs.dev.js +0 -500
- package/dist/talismn-util.cjs.js +0 -7
- package/dist/talismn-util.cjs.prod.js +0 -500
- package/dist/talismn-util.esm.js +0 -463
|
@@ -1,500 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var tailwindMerge = require('tailwind-merge');
|
|
4
|
-
var rxjs = require('rxjs');
|
|
5
|
-
var BigNumber = require('bignumber.js');
|
|
6
|
-
var lodashEs = require('lodash-es');
|
|
7
|
-
|
|
8
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
-
|
|
10
|
-
var BigNumber__default = /*#__PURE__*/_interopDefault(BigNumber);
|
|
11
|
-
|
|
12
|
-
const addTrailingSlash = url => {
|
|
13
|
-
if (url.endsWith("/")) {
|
|
14
|
-
return url;
|
|
15
|
-
}
|
|
16
|
-
return `${url}/`;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Javascript's `Math` library for `BigInt`.
|
|
21
|
-
* Taken from https://stackoverflow.com/questions/51867270/is-there-a-library-similar-to-math-that-supports-javascript-bigint/64953280#64953280
|
|
22
|
-
*/
|
|
23
|
-
const BigMath = {
|
|
24
|
-
abs(x) {
|
|
25
|
-
return x < 0n ? -x : x;
|
|
26
|
-
},
|
|
27
|
-
sign(x) {
|
|
28
|
-
if (x === 0n) return 0n;
|
|
29
|
-
return x < 0n ? -1n : 1n;
|
|
30
|
-
},
|
|
31
|
-
// TODO: Improve our babel/tsc config to let us use the `**` operator on bigint values.
|
|
32
|
-
// Error thrown: Exponentiation cannot be performed on 'bigint' values unless the 'target' option is set to 'es2016' or later. ts(2791)
|
|
33
|
-
// pow(base: bigint, exponent: bigint) {
|
|
34
|
-
// return base ** exponent
|
|
35
|
-
// },
|
|
36
|
-
min(value, ...values) {
|
|
37
|
-
for (const v of values) if (v < value) value = v;
|
|
38
|
-
return value;
|
|
39
|
-
},
|
|
40
|
-
max(value, ...values) {
|
|
41
|
-
for (const v of values) if (v > value) value = v;
|
|
42
|
-
return value;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const classNames = tailwindMerge.twMerge;
|
|
47
|
-
const cn = tailwindMerge.twMerge;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* In TypeScript, a deferred promise refers to a pattern that involves creating a promise that can be
|
|
51
|
-
* resolved or rejected at a later point in time, typically by code outside of the current function scope.
|
|
52
|
-
*
|
|
53
|
-
* This pattern is often used when dealing with asynchronous operations that involve multiple steps or when
|
|
54
|
-
* the result of an operation cannot be immediately determined.
|
|
55
|
-
*/
|
|
56
|
-
function Deferred() {
|
|
57
|
-
let resolve;
|
|
58
|
-
let reject;
|
|
59
|
-
let isPending = true;
|
|
60
|
-
let isResolved = false;
|
|
61
|
-
let isRejected = false;
|
|
62
|
-
const promise = new Promise((innerResolve, innerReject) => {
|
|
63
|
-
resolve = value => {
|
|
64
|
-
isPending = false;
|
|
65
|
-
isResolved = true;
|
|
66
|
-
innerResolve(value);
|
|
67
|
-
};
|
|
68
|
-
reject = reason => {
|
|
69
|
-
isPending = false;
|
|
70
|
-
isRejected = true;
|
|
71
|
-
innerReject(reason);
|
|
72
|
-
};
|
|
73
|
-
});
|
|
74
|
-
return {
|
|
75
|
-
promise,
|
|
76
|
-
resolve,
|
|
77
|
-
reject,
|
|
78
|
-
isPending: () => isPending,
|
|
79
|
-
isResolved: () => isResolved,
|
|
80
|
-
isRejected: () => isRejected
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* An rxjs operator which:
|
|
86
|
-
*
|
|
87
|
-
* 1. Emits the first value it receives from the source observable, then:
|
|
88
|
-
* 2. Debounces any future values by `timeout` ms.
|
|
89
|
-
*/
|
|
90
|
-
const firstThenDebounce = timeout => source => rxjs.concat(source.pipe(rxjs.take(1)), source.pipe(rxjs.skip(1)).pipe(rxjs.debounceTime(timeout)));
|
|
91
|
-
|
|
92
|
-
const MIN_DIGITS = 4; // less truncates more than what compact formating is
|
|
93
|
-
const MAX_DECIMALS_FORMAT = 12;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Custom decimal number formatting for Talisman
|
|
97
|
-
* note that the NumberFormat().format() call is the ressource heavy part, it's not worth trying to optimize other parts
|
|
98
|
-
* @param num input number
|
|
99
|
-
* @param digits number of significant digits to display
|
|
100
|
-
* @param locale locale used to format the number
|
|
101
|
-
* @param options formatting options
|
|
102
|
-
* @returns the formatted value
|
|
103
|
-
*/
|
|
104
|
-
const formatDecimals = (num, digits = MIN_DIGITS, options = {}, locale = "en-US") => {
|
|
105
|
-
if (num === null || num === undefined) return "";
|
|
106
|
-
if (digits < MIN_DIGITS) digits = MIN_DIGITS;
|
|
107
|
-
const value = new BigNumber__default.default(num);
|
|
108
|
-
// very small numbers should display "< 0.0001"
|
|
109
|
-
const minDisplayVal = 1 / Math.pow(10, digits);
|
|
110
|
-
if (value.gt(0) && value.lt(minDisplayVal)) return `< ${formatDecimals(minDisplayVal)}`;
|
|
111
|
-
|
|
112
|
-
// count digits
|
|
113
|
-
const flooredValue = value.integerValue();
|
|
114
|
-
const intDigits = flooredValue.isEqualTo(0) ? 0 : flooredValue.toString().length;
|
|
115
|
-
|
|
116
|
-
// we never want to display a rounded up value
|
|
117
|
-
// to prevent JS default rounding, we will remove/truncate insignificant digits ourselves before formatting
|
|
118
|
-
let truncatedValue = value;
|
|
119
|
-
//remove insignificant fraction digits
|
|
120
|
-
const excessFractionDigitsPow10 = new BigNumber__default.default(10).pow(digits > intDigits ? digits - intDigits : 0);
|
|
121
|
-
truncatedValue = truncatedValue.multipliedBy(excessFractionDigitsPow10).integerValue().dividedBy(excessFractionDigitsPow10);
|
|
122
|
-
|
|
123
|
-
//remove insignificant integer digits
|
|
124
|
-
const excessIntegerDigits = new BigNumber__default.default(intDigits > digits ? intDigits - digits : 0);
|
|
125
|
-
const excessIntegerDigitsPow10 = new BigNumber__default.default(10).pow(excessIntegerDigits);
|
|
126
|
-
if (excessIntegerDigits.gt(0)) truncatedValue = truncatedValue.dividedBy(excessIntegerDigitsPow10).integerValue().multipliedBy(excessIntegerDigitsPow10);
|
|
127
|
-
|
|
128
|
-
// format
|
|
129
|
-
|
|
130
|
-
return Intl.NumberFormat(locale, {
|
|
131
|
-
// compact notation (K, M, B) if above 9999
|
|
132
|
-
notation: truncatedValue.gt(9999) ? "compact" : "standard",
|
|
133
|
-
// NOTE: possible values are from `0` to `21`
|
|
134
|
-
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#maximumsignificantdigits
|
|
135
|
-
maximumSignificantDigits: Math.max(1, Math.min(digits + (truncatedValue.lt(1) ? 1 : 0), 21)),
|
|
136
|
-
...options
|
|
137
|
-
}).format(truncatedValue.toNumber());
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const formatPrice = (price, currency, compact) => {
|
|
141
|
-
return Intl.NumberFormat(undefined, {
|
|
142
|
-
style: "currency",
|
|
143
|
-
currency,
|
|
144
|
-
currencyDisplay: currency === "usd" ? "narrowSymbol" : "symbol",
|
|
145
|
-
minimumSignificantDigits: 3,
|
|
146
|
-
maximumSignificantDigits: compact ? price < 1 ? 3 : 4 : undefined,
|
|
147
|
-
roundingPriority: compact ? "auto" : "morePrecision",
|
|
148
|
-
notation: compact ? "compact" : "standard"
|
|
149
|
-
}).format(price);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const CACHE = new Map();
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* When using react-rxjs hooks and state observables, the options are used as weak map keys.
|
|
156
|
-
* This means that if the options object is recreated on each render, the observable will be recreated as well.
|
|
157
|
-
* 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
|
|
158
|
-
*
|
|
159
|
-
* @param namespace
|
|
160
|
-
* @param args
|
|
161
|
-
* @param createObservable
|
|
162
|
-
* @param serializer
|
|
163
|
-
* @returns
|
|
164
|
-
*/
|
|
165
|
-
const getSharedObservable = (namespace, args, createObservable, serializer = args => JSON.stringify(args)) => {
|
|
166
|
-
const cacheKey = `${namespace}:${serializer(args)}`;
|
|
167
|
-
if (CACHE.has(cacheKey)) return CACHE.get(cacheKey);
|
|
168
|
-
const obs = createObservable(args);
|
|
169
|
-
const sharedObs = obs.pipe(rxjs.shareReplay({
|
|
170
|
-
bufferSize: 1,
|
|
171
|
-
refCount: true
|
|
172
|
-
}));
|
|
173
|
-
CACHE.set(cacheKey, sharedObs);
|
|
174
|
-
return sharedObs;
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
function hasOwnProperty(obj, prop) {
|
|
178
|
-
if (typeof obj !== "object") return false;
|
|
179
|
-
if (obj === null) return false;
|
|
180
|
-
return prop in obj;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const isAbortError = error => {
|
|
184
|
-
return error instanceof Error && error.name === "AbortError";
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
function isArrayOf(array, func) {
|
|
188
|
-
if (array.length > 0 && array[0] instanceof func) return true;
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const isAscii = str => {
|
|
193
|
-
return [...str].every(char => char.charCodeAt(0) <= 127);
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const isBigInt = value => typeof value === "bigint";
|
|
197
|
-
|
|
198
|
-
const isBooleanTrue = x => !!x;
|
|
199
|
-
|
|
200
|
-
const REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
|
|
201
|
-
const isHexString = value => {
|
|
202
|
-
return typeof value === "string" && REGEX_HEX_STRING.test(value);
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* WARNING: This function only checks against null or undefined, it does not coerce the value.
|
|
207
|
-
* ie: false and 0 are considered not nil
|
|
208
|
-
* Use isTruthy instead for a regular coercion check.
|
|
209
|
-
*
|
|
210
|
-
* @param value
|
|
211
|
-
* @returns whether the value is neither null nor undefined
|
|
212
|
-
*/
|
|
213
|
-
const isNotNil = value => value !== null && value !== undefined;
|
|
214
|
-
|
|
215
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
216
|
-
const isPromise = value => !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Tests to see if an object is an RxJS {@link Subject}.
|
|
220
|
-
*/
|
|
221
|
-
function isSubject(object) {
|
|
222
|
-
if (!object) return false;
|
|
223
|
-
if (object instanceof rxjs.Subject) return true;
|
|
224
|
-
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);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Returns `true` if `value` is a function.
|
|
229
|
-
*/
|
|
230
|
-
function isFn(value) {
|
|
231
|
-
return typeof value === "function";
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Returns `true` if `value` is a boolean.
|
|
236
|
-
*/
|
|
237
|
-
function isBool(value) {
|
|
238
|
-
return typeof value === "boolean";
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const isTruthy = value => Boolean(value);
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* An RxJS operator that keeps the source observable alive for a specified duration
|
|
245
|
-
* after all subscribers have unsubscribed. This prevents expensive re-subscriptions
|
|
246
|
-
* when subscribers come and go frequently.
|
|
247
|
-
*
|
|
248
|
-
* @param keepAliveMs - Duration in milliseconds to keep the source alive after last unsubscription
|
|
249
|
-
* @returns MonoTypeOperatorFunction that can be used in pipe()
|
|
250
|
-
*
|
|
251
|
-
* @example
|
|
252
|
-
* ```typescript
|
|
253
|
-
* const data$ = expensive_api_call$.pipe(
|
|
254
|
-
* keepAlive(3000) // Keep alive for 3 seconds
|
|
255
|
-
* );
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
const keepAlive = timeout => {
|
|
259
|
-
let release;
|
|
260
|
-
return source => source.pipe(rxjs.tap({
|
|
261
|
-
subscribe: () => {
|
|
262
|
-
release = keepSourceSubscribed(source, timeout);
|
|
263
|
-
},
|
|
264
|
-
unsubscribe: () => {
|
|
265
|
-
release?.();
|
|
266
|
-
}
|
|
267
|
-
}), rxjs.shareReplay({
|
|
268
|
-
refCount: true,
|
|
269
|
-
bufferSize: 1
|
|
270
|
-
}));
|
|
271
|
-
};
|
|
272
|
-
const keepSourceSubscribed = (observable, ms) => {
|
|
273
|
-
const sub = observable.subscribe();
|
|
274
|
-
return () => setTimeout(() => sub.unsubscribe(), ms);
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
function planckToTokens(planck, tokenDecimals) {
|
|
278
|
-
if (typeof planck !== "string" || typeof tokenDecimals !== "number") return;
|
|
279
|
-
const base = 10;
|
|
280
|
-
const exponent = -1 * tokenDecimals;
|
|
281
|
-
const multiplier = base ** exponent;
|
|
282
|
-
return new BigNumber__default.default(planck).multipliedBy(multiplier).toString(10);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Turns a value into a {@link ReplaySubject} of size 1.
|
|
287
|
-
*
|
|
288
|
-
* If the value is already a {@link ReplaySubject}, it will be returned as-is.
|
|
289
|
-
*
|
|
290
|
-
* If the value is a {@link Promise}, it will be awaited,
|
|
291
|
-
* and the awaited value will be published into the {@link ReplaySubject} when it becomes available.
|
|
292
|
-
*
|
|
293
|
-
* For any other type of value, it will be immediately published into the {@link ReplaySubject}.
|
|
294
|
-
*/
|
|
295
|
-
const replaySubjectFrom = initialValue => {
|
|
296
|
-
if (initialValue instanceof rxjs.ReplaySubject) return initialValue;
|
|
297
|
-
const subject = new rxjs.ReplaySubject(1);
|
|
298
|
-
|
|
299
|
-
// if initialValue is a promise, await it and then call `subject.next()` with the awaited value
|
|
300
|
-
if (isPromise(initialValue)) {
|
|
301
|
-
initialValue.then(value => subject.next(value), error => subject.error(error));
|
|
302
|
-
return subject;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// if initialValue is not a promise, immediately call `subject.next()` with the value
|
|
306
|
-
subject.next(initialValue);
|
|
307
|
-
return subject;
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const sleep = ms => new Promise(resolve => {
|
|
311
|
-
setTimeout(resolve, ms);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Takes a subject and splits it into two parts:
|
|
316
|
-
*
|
|
317
|
-
* 1. A function to submit new values into the subject.
|
|
318
|
-
* 2. An observable for subscribing to new values from the subject.
|
|
319
|
-
*
|
|
320
|
-
* This can be helpful when, to avoid bugs, you want to expose only one
|
|
321
|
-
* of these parts to external code and keep the other part private.
|
|
322
|
-
*/
|
|
323
|
-
function splitSubject(subject) {
|
|
324
|
-
const next = value => subject.next(value);
|
|
325
|
-
const observable = subject.asObservable();
|
|
326
|
-
return [next, observable];
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const throwAfter = (ms, reason) => new Promise((_, reject) => setTimeout(() => reject(new Error(reason)), ms));
|
|
330
|
-
|
|
331
|
-
function tokensToPlanck(tokens, tokenDecimals) {
|
|
332
|
-
if (typeof tokens !== "string" || typeof tokenDecimals !== "number") return;
|
|
333
|
-
const base = 10;
|
|
334
|
-
const exponent = tokenDecimals;
|
|
335
|
-
const multiplier = base ** exponent;
|
|
336
|
-
return new BigNumber__default.default(tokens).multipliedBy(multiplier).toString(10);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* @name validateHexString
|
|
341
|
-
* @description Checks if a string is a hex string. Required to account for type differences between different polkadot libraries
|
|
342
|
-
* @param {string} str - string to check
|
|
343
|
-
* @returns {`0x${string}`} - boolean
|
|
344
|
-
* @example
|
|
345
|
-
* validateHexString("0x1234") // "0x1234"
|
|
346
|
-
* validateHexString("1234") // Error: Expected a hex string
|
|
347
|
-
* validateHexString(1234) // Error: Expected a string
|
|
348
|
-
**/
|
|
349
|
-
const validateHexString = str => {
|
|
350
|
-
if (typeof str !== "string") {
|
|
351
|
-
throw new Error("Expected a string");
|
|
352
|
-
}
|
|
353
|
-
if (str.startsWith("0x")) {
|
|
354
|
-
return str;
|
|
355
|
-
}
|
|
356
|
-
throw new Error("Expected a hex string");
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
// Designed to be serializable as it can be sent to the frontend
|
|
360
|
-
|
|
361
|
-
function getLoadable$(factory, options = {}) {
|
|
362
|
-
const {
|
|
363
|
-
getError,
|
|
364
|
-
refreshInterval
|
|
365
|
-
} = options;
|
|
366
|
-
const createLoadableStream = () => rxjs.from(factory()).pipe(rxjs.map(data => ({
|
|
367
|
-
status: "success",
|
|
368
|
-
data
|
|
369
|
-
})), rxjs.catchError(error => rxjs.of({
|
|
370
|
-
status: "error",
|
|
371
|
-
error: getError ? getError(error) : getGenericError(error)
|
|
372
|
-
})));
|
|
373
|
-
const source$ = refreshInterval ? rxjs.timer(0, refreshInterval).pipe(rxjs.switchMap(() => createLoadableStream())) : createLoadableStream();
|
|
374
|
-
return source$.pipe(rxjs.startWith({
|
|
375
|
-
status: "loading"
|
|
376
|
-
}));
|
|
377
|
-
}
|
|
378
|
-
const getGenericError = error => ({
|
|
379
|
-
name: "Error",
|
|
380
|
-
message: getGenericErrorMessage(error)
|
|
381
|
-
});
|
|
382
|
-
const getGenericErrorMessage = error => {
|
|
383
|
-
if (typeof error === "string") {
|
|
384
|
-
return error;
|
|
385
|
-
} else if (error instanceof Error) {
|
|
386
|
-
return error.message;
|
|
387
|
-
} else if (error && typeof error === "object" && "message" in error) {
|
|
388
|
-
return error.message;
|
|
389
|
-
}
|
|
390
|
-
return String(error) || "Unknown error";
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Creates a shared observable for executing queries with caching, loading states, and automatic refresh capabilities.
|
|
395
|
-
*
|
|
396
|
-
* @example
|
|
397
|
-
* ```typescript
|
|
398
|
-
* const userQuery$ = getQuery$({
|
|
399
|
-
* namespace: 'users',
|
|
400
|
-
* args: { userId: 123 },
|
|
401
|
-
* queryFn: async ({ userId }) => fetchUser(userId),
|
|
402
|
-
* defaultValue: null,
|
|
403
|
-
* refreshInterval: 30000
|
|
404
|
-
* });
|
|
405
|
-
*
|
|
406
|
-
* userQuery$.subscribe(result => {
|
|
407
|
-
* if (result.status === 'loaded') {
|
|
408
|
-
* console.log(result.data);
|
|
409
|
-
* }
|
|
410
|
-
* });
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
const getQuery$ = ({
|
|
414
|
-
namespace,
|
|
415
|
-
args,
|
|
416
|
-
queryFn,
|
|
417
|
-
defaultValue,
|
|
418
|
-
refreshInterval,
|
|
419
|
-
serializer = args => JSON.stringify(args)
|
|
420
|
-
}) => {
|
|
421
|
-
return getSharedObservable(namespace, args, () => new rxjs.Observable(subscriber => {
|
|
422
|
-
const controller = new AbortController();
|
|
423
|
-
const result = new rxjs.BehaviorSubject({
|
|
424
|
-
status: "loading",
|
|
425
|
-
data: defaultValue,
|
|
426
|
-
error: undefined
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// result subscription
|
|
430
|
-
const sub = result.pipe(rxjs.distinctUntilChanged(lodashEs.isEqual)).subscribe(subscriber);
|
|
431
|
-
|
|
432
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
433
|
-
let timeout = null;
|
|
434
|
-
|
|
435
|
-
// fetch result subscription
|
|
436
|
-
const run = () => {
|
|
437
|
-
if (controller.signal.aborted) return;
|
|
438
|
-
queryFn(args, controller.signal).then(data => {
|
|
439
|
-
if (controller.signal.aborted) return;
|
|
440
|
-
result.next({
|
|
441
|
-
status: "loaded",
|
|
442
|
-
data,
|
|
443
|
-
error: undefined
|
|
444
|
-
});
|
|
445
|
-
}).catch(error => {
|
|
446
|
-
if (controller.signal.aborted) return;
|
|
447
|
-
result.next({
|
|
448
|
-
status: "error",
|
|
449
|
-
data: undefined,
|
|
450
|
-
error
|
|
451
|
-
});
|
|
452
|
-
}).finally(() => {
|
|
453
|
-
if (controller.signal.aborted) return;
|
|
454
|
-
if (refreshInterval) timeout = setTimeout(run, refreshInterval);
|
|
455
|
-
});
|
|
456
|
-
};
|
|
457
|
-
run();
|
|
458
|
-
return () => {
|
|
459
|
-
sub.unsubscribe();
|
|
460
|
-
if (timeout) clearTimeout(timeout);
|
|
461
|
-
controller.abort(new Error("getQuery$ unsubscribed"));
|
|
462
|
-
};
|
|
463
|
-
}).pipe(rxjs.shareReplay({
|
|
464
|
-
refCount: true,
|
|
465
|
-
bufferSize: 1
|
|
466
|
-
})), serializer);
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
exports.BigMath = BigMath;
|
|
470
|
-
exports.Deferred = Deferred;
|
|
471
|
-
exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
|
|
472
|
-
exports.REGEX_HEX_STRING = REGEX_HEX_STRING;
|
|
473
|
-
exports.addTrailingSlash = addTrailingSlash;
|
|
474
|
-
exports.classNames = classNames;
|
|
475
|
-
exports.cn = cn;
|
|
476
|
-
exports.firstThenDebounce = firstThenDebounce;
|
|
477
|
-
exports.formatDecimals = formatDecimals;
|
|
478
|
-
exports.formatPrice = formatPrice;
|
|
479
|
-
exports.getLoadable$ = getLoadable$;
|
|
480
|
-
exports.getQuery$ = getQuery$;
|
|
481
|
-
exports.getSharedObservable = getSharedObservable;
|
|
482
|
-
exports.hasOwnProperty = hasOwnProperty;
|
|
483
|
-
exports.isAbortError = isAbortError;
|
|
484
|
-
exports.isArrayOf = isArrayOf;
|
|
485
|
-
exports.isAscii = isAscii;
|
|
486
|
-
exports.isBigInt = isBigInt;
|
|
487
|
-
exports.isBooleanTrue = isBooleanTrue;
|
|
488
|
-
exports.isHexString = isHexString;
|
|
489
|
-
exports.isNotNil = isNotNil;
|
|
490
|
-
exports.isPromise = isPromise;
|
|
491
|
-
exports.isSubject = isSubject;
|
|
492
|
-
exports.isTruthy = isTruthy;
|
|
493
|
-
exports.keepAlive = keepAlive;
|
|
494
|
-
exports.planckToTokens = planckToTokens;
|
|
495
|
-
exports.replaySubjectFrom = replaySubjectFrom;
|
|
496
|
-
exports.sleep = sleep;
|
|
497
|
-
exports.splitSubject = splitSubject;
|
|
498
|
-
exports.throwAfter = throwAfter;
|
|
499
|
-
exports.tokensToPlanck = tokensToPlanck;
|
|
500
|
-
exports.validateHexString = validateHexString;
|