@talismn/util 0.4.2 → 0.5.1
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/declarations/src/Prettify.d.ts +3 -0
- package/dist/declarations/src/classNames.d.ts +1 -0
- package/dist/declarations/src/getLoadable.d.ts +25 -0
- package/dist/declarations/src/getSharedObservable.d.ts +13 -0
- package/dist/declarations/src/index.d.ts +13 -13
- package/dist/declarations/src/isAbortError.d.ts +1 -0
- package/dist/declarations/src/isHexString.d.ts +3 -0
- package/dist/declarations/src/isPromise.d.ts +1 -0
- package/dist/declarations/src/isSubject.d.ts +5 -0
- package/dist/declarations/src/keepAlive.d.ts +17 -0
- package/dist/declarations/src/replaySubjectFrom.d.ts +12 -0
- package/dist/declarations/src/splitSubject.d.ts +11 -0
- package/dist/declarations/src/validateHexString.d.ts +2 -3
- package/dist/talismn-util.cjs.dev.js +189 -117
- package/dist/talismn-util.cjs.prod.js +189 -117
- package/dist/talismn-util.esm.js +180 -109
- package/package.json +3 -9
- package/dist/declarations/src/blake2Concat.d.ts +0 -1
- package/dist/declarations/src/convertAddress.d.ts +0 -7
- package/dist/declarations/src/decodeAnyAddress.d.ts +0 -1
- package/dist/declarations/src/decodeSs58Format.d.ts +0 -1
- package/dist/declarations/src/encodeAnyAddress.d.ts +0 -1
- package/dist/declarations/src/isAddressEqual.d.ts +0 -1
- package/dist/declarations/src/isEthereumAddress.d.ts +0 -1
- package/dist/declarations/src/isValidSubstrateAddress.d.ts +0 -7
- package/dist/declarations/src/normalizeAddress.d.ts +0 -1
- package/dist/declarations/src/twox64Concat.d.ts +0 -1
@@ -1,9 +1,6 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
|
-
var util = require('@polkadot/util');
|
4
|
-
var utilCrypto = require('@polkadot/util-crypto');
|
5
3
|
var tailwindMerge = require('tailwind-merge');
|
6
|
-
var keyring = require('@polkadot/keyring');
|
7
4
|
var rxjs = require('rxjs');
|
8
5
|
var BigNumber = require('bignumber.js');
|
9
6
|
|
@@ -45,54 +42,8 @@ const BigMath = {
|
|
45
42
|
}
|
46
43
|
};
|
47
44
|
|
48
|
-
const bitLength$1 = 128;
|
49
|
-
function blake2Concat(input) {
|
50
|
-
return util.u8aToHex(util.u8aConcat(utilCrypto.blake2AsU8a(input, bitLength$1), util.u8aToU8a(input)));
|
51
|
-
}
|
52
|
-
|
53
45
|
const classNames = tailwindMerge.twMerge;
|
54
|
-
|
55
|
-
function encodeAnyAddress(key, ss58Format) {
|
56
|
-
try {
|
57
|
-
return keyring.encodeAddress(key, ss58Format);
|
58
|
-
} catch (error) {
|
59
|
-
if (typeof key !== "string") throw error;
|
60
|
-
if (!utilCrypto.isEthereumAddress(key)) throw error;
|
61
|
-
return utilCrypto.ethereumEncode(key);
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
/**
|
66
|
-
*
|
67
|
-
* @param address substrate SS58 address
|
68
|
-
* @param prefix prefix used to format the address
|
69
|
-
* @returns address encoded with supplied prefix
|
70
|
-
*/
|
71
|
-
const convertAddress = (address, prefix) => {
|
72
|
-
return encodeAnyAddress(address, prefix ?? undefined);
|
73
|
-
};
|
74
|
-
|
75
|
-
function decodeAnyAddress(encoded, ignoreChecksum, ss58Format) {
|
76
|
-
try {
|
77
|
-
return keyring.decodeAddress(encoded, ignoreChecksum, ss58Format);
|
78
|
-
} catch (error) {
|
79
|
-
if (typeof encoded !== "string") throw error;
|
80
|
-
if (!utilCrypto.isEthereumAddress(encoded)) throw error;
|
81
|
-
return util.hexToU8a(encoded.slice("0x".length));
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
const decodeSs58Format = address => {
|
86
|
-
if (!address) return;
|
87
|
-
try {
|
88
|
-
utilCrypto.decodeAddress(address);
|
89
|
-
const decoded = utilCrypto.base58Decode(address);
|
90
|
-
const [,,, ss58Format] = utilCrypto.checkAddressChecksum(decoded);
|
91
|
-
return ss58Format;
|
92
|
-
} catch {
|
93
|
-
return; // invalid address
|
94
|
-
}
|
95
|
-
};
|
46
|
+
const cn = tailwindMerge.twMerge;
|
96
47
|
|
97
48
|
/**
|
98
49
|
* In TypeScript, a deferred promise refers to a pattern that involves creating a promise that can be
|
@@ -195,43 +146,129 @@ const formatPrice = (price, currency, compact) => {
|
|
195
146
|
}).format(price);
|
196
147
|
};
|
197
148
|
|
149
|
+
const CACHE = new Map();
|
150
|
+
|
151
|
+
/**
|
152
|
+
* When using react-rxjs hooks and state observables, the options are used as weak map keys.
|
153
|
+
* This means that if the options object is recreated on each render, the observable will be recreated as well.
|
154
|
+
* 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
|
155
|
+
*
|
156
|
+
* @param namespace
|
157
|
+
* @param args
|
158
|
+
* @param createObservable
|
159
|
+
* @param serializer
|
160
|
+
* @returns
|
161
|
+
*/
|
162
|
+
const getSharedObservable = (namespace, args, createObservable, serializer = args => JSON.stringify(args)) => {
|
163
|
+
const cacheKey = `${namespace}:${serializer(args)}`;
|
164
|
+
if (CACHE.has(cacheKey)) return CACHE.get(cacheKey);
|
165
|
+
const obs = createObservable(args);
|
166
|
+
const sharedObs = obs.pipe(rxjs.shareReplay({
|
167
|
+
bufferSize: 1,
|
168
|
+
refCount: true
|
169
|
+
}));
|
170
|
+
CACHE.set(cacheKey, sharedObs);
|
171
|
+
return sharedObs;
|
172
|
+
};
|
173
|
+
|
198
174
|
function hasOwnProperty(obj, prop) {
|
199
175
|
if (typeof obj !== "object") return false;
|
200
176
|
if (obj === null) return false;
|
201
177
|
return prop in obj;
|
202
178
|
}
|
203
179
|
|
180
|
+
const isAbortError = error => {
|
181
|
+
return error instanceof Error && error.name === "AbortError";
|
182
|
+
};
|
183
|
+
|
204
184
|
function isArrayOf(array, func) {
|
205
185
|
if (array.length > 0 && array[0] instanceof func) return true;
|
206
186
|
return false;
|
207
187
|
}
|
208
188
|
|
189
|
+
const isAscii = str => {
|
190
|
+
return [...str].every(char => char.charCodeAt(0) <= 127);
|
191
|
+
};
|
192
|
+
|
209
193
|
const isBigInt = value => typeof value === "bigint";
|
210
194
|
|
211
195
|
const isBooleanTrue = x => !!x;
|
212
196
|
|
213
|
-
const
|
197
|
+
const REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
|
198
|
+
const isHexString = value => {
|
199
|
+
return typeof value === "string" && REGEX_HEX_STRING.test(value);
|
200
|
+
};
|
214
201
|
|
215
202
|
/**
|
216
|
-
*
|
217
|
-
*
|
218
|
-
*
|
219
|
-
*
|
203
|
+
* WARNING: This function only checks against null or undefined, it does not coerce the value.
|
204
|
+
* ie: false and 0 are considered not nil
|
205
|
+
* Use isTruthy instead for a regular coercion check.
|
206
|
+
*
|
207
|
+
* @param value
|
208
|
+
* @returns whether the value is neither null nor undefined
|
220
209
|
*/
|
221
|
-
const
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
210
|
+
const isNotNil = value => value !== null && value !== undefined;
|
211
|
+
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
213
|
+
const isPromise = value => !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
214
|
+
|
215
|
+
/**
|
216
|
+
* Tests to see if an object is an RxJS {@link Subject}.
|
217
|
+
*/
|
218
|
+
function isSubject(object) {
|
219
|
+
if (!object) return false;
|
220
|
+
if (object instanceof rxjs.Subject) return true;
|
221
|
+
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);
|
222
|
+
}
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Returns `true` if `value` is a function.
|
226
|
+
*/
|
227
|
+
function isFn(value) {
|
228
|
+
return typeof value === "function";
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Returns `true` if `value` is a boolean.
|
233
|
+
*/
|
234
|
+
function isBool(value) {
|
235
|
+
return typeof value === "boolean";
|
236
|
+
}
|
237
|
+
|
238
|
+
const isTruthy = value => Boolean(value);
|
239
|
+
|
240
|
+
/**
|
241
|
+
* An RxJS operator that keeps the source observable alive for a specified duration
|
242
|
+
* after all subscribers have unsubscribed. This prevents expensive re-subscriptions
|
243
|
+
* when subscribers come and go frequently.
|
244
|
+
*
|
245
|
+
* @param keepAliveMs - Duration in milliseconds to keep the source alive after last unsubscription
|
246
|
+
* @returns MonoTypeOperatorFunction that can be used in pipe()
|
247
|
+
*
|
248
|
+
* @example
|
249
|
+
* ```typescript
|
250
|
+
* const data$ = expensive_api_call$.pipe(
|
251
|
+
* keepAlive(3000) // Keep alive for 3 seconds
|
252
|
+
* );
|
253
|
+
* ```
|
254
|
+
*/
|
255
|
+
const keepAlive = timeout => {
|
256
|
+
let release;
|
257
|
+
return source => source.pipe(rxjs.tap({
|
258
|
+
subscribe: () => {
|
259
|
+
release = keepSourceSubscribed(source, timeout);
|
260
|
+
},
|
261
|
+
unsubscribe: () => {
|
262
|
+
release?.();
|
263
|
+
}
|
264
|
+
}), rxjs.shareReplay({
|
265
|
+
refCount: true,
|
266
|
+
bufferSize: 1
|
267
|
+
}));
|
268
|
+
};
|
269
|
+
const keepSourceSubscribed = (observable, ms) => {
|
270
|
+
const sub = observable.subscribe();
|
271
|
+
return () => setTimeout(() => sub.unsubscribe(), ms);
|
235
272
|
};
|
236
273
|
|
237
274
|
function planckToTokens(planck, tokenDecimals) {
|
@@ -242,10 +279,50 @@ function planckToTokens(planck, tokenDecimals) {
|
|
242
279
|
return new BigNumber__default.default(planck).multipliedBy(multiplier).toString(10);
|
243
280
|
}
|
244
281
|
|
282
|
+
/**
|
283
|
+
* Turns a value into a {@link ReplaySubject} of size 1.
|
284
|
+
*
|
285
|
+
* If the value is already a {@link ReplaySubject}, it will be returned as-is.
|
286
|
+
*
|
287
|
+
* If the value is a {@link Promise}, it will be awaited,
|
288
|
+
* and the awaited value will be published into the {@link ReplaySubject} when it becomes available.
|
289
|
+
*
|
290
|
+
* For any other type of value, it will be immediately published into the {@link ReplaySubject}.
|
291
|
+
*/
|
292
|
+
const replaySubjectFrom = initialValue => {
|
293
|
+
if (initialValue instanceof rxjs.ReplaySubject) return initialValue;
|
294
|
+
const subject = new rxjs.ReplaySubject(1);
|
295
|
+
|
296
|
+
// if initialValue is a promise, await it and then call `subject.next()` with the awaited value
|
297
|
+
if (isPromise(initialValue)) {
|
298
|
+
initialValue.then(value => subject.next(value), error => subject.error(error));
|
299
|
+
return subject;
|
300
|
+
}
|
301
|
+
|
302
|
+
// if initialValue is not a promise, immediately call `subject.next()` with the value
|
303
|
+
subject.next(initialValue);
|
304
|
+
return subject;
|
305
|
+
};
|
306
|
+
|
245
307
|
const sleep = ms => new Promise(resolve => {
|
246
308
|
setTimeout(resolve, ms);
|
247
309
|
});
|
248
310
|
|
311
|
+
/**
|
312
|
+
* Takes a subject and splits it into two parts:
|
313
|
+
*
|
314
|
+
* 1. A function to submit new values into the subject.
|
315
|
+
* 2. An observable for subscribing to new values from the subject.
|
316
|
+
*
|
317
|
+
* This can be helpful when, to avoid bugs, you want to expose only one
|
318
|
+
* of these parts to external code and keep the other part private.
|
319
|
+
*/
|
320
|
+
function splitSubject(subject) {
|
321
|
+
const next = value => subject.next(value);
|
322
|
+
const observable = subject.asObservable();
|
323
|
+
return [next, observable];
|
324
|
+
}
|
325
|
+
|
249
326
|
const throwAfter = (ms, reason) => new Promise((_, reject) => setTimeout(() => reject(new Error(reason)), ms));
|
250
327
|
|
251
328
|
function tokensToPlanck(tokens, tokenDecimals) {
|
@@ -256,17 +333,11 @@ function tokensToPlanck(tokens, tokenDecimals) {
|
|
256
333
|
return new BigNumber__default.default(tokens).multipliedBy(multiplier).toString(10);
|
257
334
|
}
|
258
335
|
|
259
|
-
const bitLength = 64;
|
260
|
-
function twox64Concat(input) {
|
261
|
-
const inputAsU8a = typeof input === "string" ? input : new Uint8Array(input);
|
262
|
-
return util.u8aToHex(util.u8aConcat(utilCrypto.xxhashAsU8a(inputAsU8a, bitLength), util.u8aToU8a(inputAsU8a)));
|
263
|
-
}
|
264
|
-
|
265
336
|
/**
|
266
337
|
* @name validateHexString
|
267
338
|
* @description Checks if a string is a hex string. Required to account for type differences between different polkadot libraries
|
268
339
|
* @param {string} str - string to check
|
269
|
-
* @returns {
|
340
|
+
* @returns {`0x${string}`} - boolean
|
270
341
|
* @example
|
271
342
|
* validateHexString("0x1234") // "0x1234"
|
272
343
|
* validateHexString("1234") // Error: Expected a hex string
|
@@ -282,67 +353,68 @@ const validateHexString = str => {
|
|
282
353
|
throw new Error("Expected a hex string");
|
283
354
|
};
|
284
355
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
const
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
356
|
+
// Designed to be serializable as it can be sent to the frontend
|
357
|
+
|
358
|
+
function getLoadable$(factory, options = {}) {
|
359
|
+
const {
|
360
|
+
getError,
|
361
|
+
refreshInterval
|
362
|
+
} = options;
|
363
|
+
const createLoadableStream = () => rxjs.from(factory()).pipe(rxjs.map(data => ({
|
364
|
+
status: "success",
|
365
|
+
data
|
366
|
+
})), rxjs.catchError(error => rxjs.of({
|
367
|
+
status: "error",
|
368
|
+
error: getError ? getError(error) : getGenericError(error)
|
369
|
+
})));
|
370
|
+
const source$ = refreshInterval ? rxjs.timer(0, refreshInterval).pipe(rxjs.switchMap(() => createLoadableStream())) : createLoadableStream();
|
371
|
+
return source$.pipe(rxjs.startWith({
|
372
|
+
status: "loading"
|
373
|
+
}));
|
374
|
+
}
|
375
|
+
const getGenericError = error => ({
|
376
|
+
name: "Error",
|
377
|
+
message: getGenericErrorMessage(error)
|
378
|
+
});
|
379
|
+
const getGenericErrorMessage = error => {
|
380
|
+
if (typeof error === "string") {
|
381
|
+
return error;
|
382
|
+
} else if (error instanceof Error) {
|
383
|
+
return error.message;
|
384
|
+
} else if (error && typeof error === "object" && "message" in error) {
|
385
|
+
return error.message;
|
296
386
|
}
|
387
|
+
return String(error) || "Unknown error";
|
297
388
|
};
|
298
389
|
|
299
|
-
const isAddressEqual = (address1, address2) => {
|
300
|
-
return normalizeAddress(address1) === normalizeAddress(address2);
|
301
|
-
};
|
302
|
-
|
303
|
-
const isAscii = str => {
|
304
|
-
return [...str].every(char => char.charCodeAt(0) <= 127);
|
305
|
-
};
|
306
|
-
|
307
|
-
/**
|
308
|
-
* WARNING: This function only checks against null or undefined, it does not coerce the value.
|
309
|
-
* ie: false and 0 are considered not nil
|
310
|
-
* Use isTruthy instead for a regular coercion check.
|
311
|
-
*
|
312
|
-
* @param value
|
313
|
-
* @returns whether the value is neither null nor undefined
|
314
|
-
*/
|
315
|
-
const isNotNil = value => value !== null && value !== undefined;
|
316
|
-
|
317
|
-
const isTruthy = value => Boolean(value);
|
318
|
-
|
319
390
|
exports.BigMath = BigMath;
|
320
391
|
exports.Deferred = Deferred;
|
321
392
|
exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
|
393
|
+
exports.REGEX_HEX_STRING = REGEX_HEX_STRING;
|
322
394
|
exports.addTrailingSlash = addTrailingSlash;
|
323
|
-
exports.blake2Concat = blake2Concat;
|
324
395
|
exports.classNames = classNames;
|
325
|
-
exports.
|
326
|
-
exports.decodeAnyAddress = decodeAnyAddress;
|
327
|
-
exports.decodeSs58Format = decodeSs58Format;
|
328
|
-
exports.encodeAnyAddress = encodeAnyAddress;
|
396
|
+
exports.cn = cn;
|
329
397
|
exports.firstThenDebounce = firstThenDebounce;
|
330
398
|
exports.formatDecimals = formatDecimals;
|
331
399
|
exports.formatPrice = formatPrice;
|
400
|
+
exports.getLoadable$ = getLoadable$;
|
401
|
+
exports.getSharedObservable = getSharedObservable;
|
332
402
|
exports.hasOwnProperty = hasOwnProperty;
|
333
|
-
exports.
|
403
|
+
exports.isAbortError = isAbortError;
|
334
404
|
exports.isArrayOf = isArrayOf;
|
335
405
|
exports.isAscii = isAscii;
|
336
406
|
exports.isBigInt = isBigInt;
|
337
407
|
exports.isBooleanTrue = isBooleanTrue;
|
338
|
-
exports.
|
408
|
+
exports.isHexString = isHexString;
|
339
409
|
exports.isNotNil = isNotNil;
|
410
|
+
exports.isPromise = isPromise;
|
411
|
+
exports.isSubject = isSubject;
|
340
412
|
exports.isTruthy = isTruthy;
|
341
|
-
exports.
|
342
|
-
exports.normalizeAddress = normalizeAddress;
|
413
|
+
exports.keepAlive = keepAlive;
|
343
414
|
exports.planckToTokens = planckToTokens;
|
415
|
+
exports.replaySubjectFrom = replaySubjectFrom;
|
344
416
|
exports.sleep = sleep;
|
417
|
+
exports.splitSubject = splitSubject;
|
345
418
|
exports.throwAfter = throwAfter;
|
346
419
|
exports.tokensToPlanck = tokensToPlanck;
|
347
|
-
exports.twox64Concat = twox64Concat;
|
348
420
|
exports.validateHexString = validateHexString;
|