@talismn/util 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/declarations/src/getSharedObservable.d.ts +13 -0
- package/dist/declarations/src/index.d.ts +4 -0
- package/dist/declarations/src/isAbortError.d.ts +1 -0
- package/dist/declarations/src/isHexString.d.ts +3 -0
- package/dist/declarations/src/keepAlive.d.ts +17 -0
- package/dist/talismn-util.cjs.dev.js +76 -3
- package/dist/talismn-util.cjs.prod.js +76 -3
- package/dist/talismn-util.esm.js +73 -5
- package/package.json +6 -6
@@ -0,0 +1,13 @@
|
|
1
|
+
import { Observable } from "rxjs";
|
2
|
+
/**
|
3
|
+
* When using react-rxjs hooks and state observables, the options are used as weak map keys.
|
4
|
+
* This means that if the options object is recreated on each render, the observable will be recreated as well.
|
5
|
+
* 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
|
6
|
+
*
|
7
|
+
* @param namespace
|
8
|
+
* @param args
|
9
|
+
* @param createObservable
|
10
|
+
* @param serializer
|
11
|
+
* @returns
|
12
|
+
*/
|
13
|
+
export declare const getSharedObservable: <Args, Output, ObsOutput = Observable<Output>>(namespace: string, args: Args, createObservable: (args: Args) => ObsOutput, serializer?: (args: Args) => string) => ObsOutput;
|
@@ -28,3 +28,7 @@ export * from "./isAddressEqual";
|
|
28
28
|
export * from "./isAscii";
|
29
29
|
export * from "./isNotNil";
|
30
30
|
export * from "./isTruthy";
|
31
|
+
export * from "./isAbortError";
|
32
|
+
export * from "./getSharedObservable";
|
33
|
+
export * from "./keepAlive";
|
34
|
+
export * from "./isHexString";
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const isAbortError: (error: unknown) => boolean;
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import type { OperatorFunction } from "rxjs";
|
2
|
+
/**
|
3
|
+
* An RxJS operator that keeps the source observable alive for a specified duration
|
4
|
+
* after all subscribers have unsubscribed. This prevents expensive re-subscriptions
|
5
|
+
* when subscribers come and go frequently.
|
6
|
+
*
|
7
|
+
* @param keepAliveMs - Duration in milliseconds to keep the source alive after last unsubscription
|
8
|
+
* @returns MonoTypeOperatorFunction that can be used in pipe()
|
9
|
+
*
|
10
|
+
* @example
|
11
|
+
* ```typescript
|
12
|
+
* const data$ = expensive_api_call$.pipe(
|
13
|
+
* keepAlive(3000) // Keep alive for 3 seconds
|
14
|
+
* );
|
15
|
+
* ```
|
16
|
+
*/
|
17
|
+
export declare const keepAlive: <T>(timeout: number) => OperatorFunction<T, T>;
|
@@ -282,13 +282,13 @@ const validateHexString = str => {
|
|
282
282
|
throw new Error("Expected a hex string");
|
283
283
|
};
|
284
284
|
|
285
|
-
const CACHE = new Map();
|
285
|
+
const CACHE$1 = new Map();
|
286
286
|
|
287
287
|
// Normalize an address in a way that it can be compared to other addresses that have also been normalized
|
288
288
|
const normalizeAddress = address => {
|
289
289
|
try {
|
290
|
-
if (!CACHE.has(address)) CACHE.set(address, encodeAnyAddress(address));
|
291
|
-
return CACHE.get(address);
|
290
|
+
if (!CACHE$1.has(address)) CACHE$1.set(address, encodeAnyAddress(address));
|
291
|
+
return CACHE$1.get(address);
|
292
292
|
} catch (cause) {
|
293
293
|
throw new Error(`Unable to normalize address: ${address}`, {
|
294
294
|
cause
|
@@ -316,9 +316,78 @@ const isNotNil = value => value !== null && value !== undefined;
|
|
316
316
|
|
317
317
|
const isTruthy = value => Boolean(value);
|
318
318
|
|
319
|
+
const isAbortError = error => {
|
320
|
+
return error instanceof Error && error.name === "AbortError";
|
321
|
+
};
|
322
|
+
|
323
|
+
const CACHE = new Map();
|
324
|
+
|
325
|
+
/**
|
326
|
+
* When using react-rxjs hooks and state observables, the options are used as weak map keys.
|
327
|
+
* This means that if the options object is recreated on each render, the observable will be recreated as well.
|
328
|
+
* 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
|
329
|
+
*
|
330
|
+
* @param namespace
|
331
|
+
* @param args
|
332
|
+
* @param createObservable
|
333
|
+
* @param serializer
|
334
|
+
* @returns
|
335
|
+
*/
|
336
|
+
const getSharedObservable = (namespace, args, createObservable, serializer = args => JSON.stringify(args)) => {
|
337
|
+
const cacheKey = `${namespace}:${serializer(args)}`;
|
338
|
+
if (CACHE.has(cacheKey)) return CACHE.get(cacheKey);
|
339
|
+
const obs = createObservable(args);
|
340
|
+
const sharedObs = obs.pipe(rxjs.shareReplay({
|
341
|
+
bufferSize: 1,
|
342
|
+
refCount: true
|
343
|
+
}));
|
344
|
+
CACHE.set(cacheKey, sharedObs);
|
345
|
+
return sharedObs;
|
346
|
+
};
|
347
|
+
|
348
|
+
/**
|
349
|
+
* An RxJS operator that keeps the source observable alive for a specified duration
|
350
|
+
* after all subscribers have unsubscribed. This prevents expensive re-subscriptions
|
351
|
+
* when subscribers come and go frequently.
|
352
|
+
*
|
353
|
+
* @param keepAliveMs - Duration in milliseconds to keep the source alive after last unsubscription
|
354
|
+
* @returns MonoTypeOperatorFunction that can be used in pipe()
|
355
|
+
*
|
356
|
+
* @example
|
357
|
+
* ```typescript
|
358
|
+
* const data$ = expensive_api_call$.pipe(
|
359
|
+
* keepAlive(3000) // Keep alive for 3 seconds
|
360
|
+
* );
|
361
|
+
* ```
|
362
|
+
*/
|
363
|
+
const keepAlive = timeout => {
|
364
|
+
let release;
|
365
|
+
return source => source.pipe(rxjs.tap({
|
366
|
+
subscribe: () => {
|
367
|
+
release = keepSourceSubscribed(source, timeout);
|
368
|
+
},
|
369
|
+
unsubscribe: () => {
|
370
|
+
release?.();
|
371
|
+
}
|
372
|
+
}), rxjs.shareReplay({
|
373
|
+
refCount: true,
|
374
|
+
bufferSize: 1
|
375
|
+
}));
|
376
|
+
};
|
377
|
+
const keepSourceSubscribed = (observable, ms) => {
|
378
|
+
const sub = observable.subscribe();
|
379
|
+
return () => setTimeout(() => sub.unsubscribe(), ms);
|
380
|
+
};
|
381
|
+
|
382
|
+
const REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
|
383
|
+
const isHexString = value => {
|
384
|
+
return typeof value === "string" && REGEX_HEX_STRING.test(value);
|
385
|
+
};
|
386
|
+
|
319
387
|
exports.BigMath = BigMath;
|
320
388
|
exports.Deferred = Deferred;
|
321
389
|
exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
|
390
|
+
exports.REGEX_HEX_STRING = REGEX_HEX_STRING;
|
322
391
|
exports.addTrailingSlash = addTrailingSlash;
|
323
392
|
exports.blake2Concat = blake2Concat;
|
324
393
|
exports.classNames = classNames;
|
@@ -329,16 +398,20 @@ exports.encodeAnyAddress = encodeAnyAddress;
|
|
329
398
|
exports.firstThenDebounce = firstThenDebounce;
|
330
399
|
exports.formatDecimals = formatDecimals;
|
331
400
|
exports.formatPrice = formatPrice;
|
401
|
+
exports.getSharedObservable = getSharedObservable;
|
332
402
|
exports.hasOwnProperty = hasOwnProperty;
|
403
|
+
exports.isAbortError = isAbortError;
|
333
404
|
exports.isAddressEqual = isAddressEqual;
|
334
405
|
exports.isArrayOf = isArrayOf;
|
335
406
|
exports.isAscii = isAscii;
|
336
407
|
exports.isBigInt = isBigInt;
|
337
408
|
exports.isBooleanTrue = isBooleanTrue;
|
338
409
|
exports.isEthereumAddress = isEthereumAddress;
|
410
|
+
exports.isHexString = isHexString;
|
339
411
|
exports.isNotNil = isNotNil;
|
340
412
|
exports.isTruthy = isTruthy;
|
341
413
|
exports.isValidSubstrateAddress = isValidSubstrateAddress;
|
414
|
+
exports.keepAlive = keepAlive;
|
342
415
|
exports.normalizeAddress = normalizeAddress;
|
343
416
|
exports.planckToTokens = planckToTokens;
|
344
417
|
exports.sleep = sleep;
|
@@ -282,13 +282,13 @@ const validateHexString = str => {
|
|
282
282
|
throw new Error("Expected a hex string");
|
283
283
|
};
|
284
284
|
|
285
|
-
const CACHE = new Map();
|
285
|
+
const CACHE$1 = new Map();
|
286
286
|
|
287
287
|
// Normalize an address in a way that it can be compared to other addresses that have also been normalized
|
288
288
|
const normalizeAddress = address => {
|
289
289
|
try {
|
290
|
-
if (!CACHE.has(address)) CACHE.set(address, encodeAnyAddress(address));
|
291
|
-
return CACHE.get(address);
|
290
|
+
if (!CACHE$1.has(address)) CACHE$1.set(address, encodeAnyAddress(address));
|
291
|
+
return CACHE$1.get(address);
|
292
292
|
} catch (cause) {
|
293
293
|
throw new Error(`Unable to normalize address: ${address}`, {
|
294
294
|
cause
|
@@ -316,9 +316,78 @@ const isNotNil = value => value !== null && value !== undefined;
|
|
316
316
|
|
317
317
|
const isTruthy = value => Boolean(value);
|
318
318
|
|
319
|
+
const isAbortError = error => {
|
320
|
+
return error instanceof Error && error.name === "AbortError";
|
321
|
+
};
|
322
|
+
|
323
|
+
const CACHE = new Map();
|
324
|
+
|
325
|
+
/**
|
326
|
+
* When using react-rxjs hooks and state observables, the options are used as weak map keys.
|
327
|
+
* This means that if the options object is recreated on each render, the observable will be recreated as well.
|
328
|
+
* 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
|
329
|
+
*
|
330
|
+
* @param namespace
|
331
|
+
* @param args
|
332
|
+
* @param createObservable
|
333
|
+
* @param serializer
|
334
|
+
* @returns
|
335
|
+
*/
|
336
|
+
const getSharedObservable = (namespace, args, createObservable, serializer = args => JSON.stringify(args)) => {
|
337
|
+
const cacheKey = `${namespace}:${serializer(args)}`;
|
338
|
+
if (CACHE.has(cacheKey)) return CACHE.get(cacheKey);
|
339
|
+
const obs = createObservable(args);
|
340
|
+
const sharedObs = obs.pipe(rxjs.shareReplay({
|
341
|
+
bufferSize: 1,
|
342
|
+
refCount: true
|
343
|
+
}));
|
344
|
+
CACHE.set(cacheKey, sharedObs);
|
345
|
+
return sharedObs;
|
346
|
+
};
|
347
|
+
|
348
|
+
/**
|
349
|
+
* An RxJS operator that keeps the source observable alive for a specified duration
|
350
|
+
* after all subscribers have unsubscribed. This prevents expensive re-subscriptions
|
351
|
+
* when subscribers come and go frequently.
|
352
|
+
*
|
353
|
+
* @param keepAliveMs - Duration in milliseconds to keep the source alive after last unsubscription
|
354
|
+
* @returns MonoTypeOperatorFunction that can be used in pipe()
|
355
|
+
*
|
356
|
+
* @example
|
357
|
+
* ```typescript
|
358
|
+
* const data$ = expensive_api_call$.pipe(
|
359
|
+
* keepAlive(3000) // Keep alive for 3 seconds
|
360
|
+
* );
|
361
|
+
* ```
|
362
|
+
*/
|
363
|
+
const keepAlive = timeout => {
|
364
|
+
let release;
|
365
|
+
return source => source.pipe(rxjs.tap({
|
366
|
+
subscribe: () => {
|
367
|
+
release = keepSourceSubscribed(source, timeout);
|
368
|
+
},
|
369
|
+
unsubscribe: () => {
|
370
|
+
release?.();
|
371
|
+
}
|
372
|
+
}), rxjs.shareReplay({
|
373
|
+
refCount: true,
|
374
|
+
bufferSize: 1
|
375
|
+
}));
|
376
|
+
};
|
377
|
+
const keepSourceSubscribed = (observable, ms) => {
|
378
|
+
const sub = observable.subscribe();
|
379
|
+
return () => setTimeout(() => sub.unsubscribe(), ms);
|
380
|
+
};
|
381
|
+
|
382
|
+
const REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
|
383
|
+
const isHexString = value => {
|
384
|
+
return typeof value === "string" && REGEX_HEX_STRING.test(value);
|
385
|
+
};
|
386
|
+
|
319
387
|
exports.BigMath = BigMath;
|
320
388
|
exports.Deferred = Deferred;
|
321
389
|
exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
|
390
|
+
exports.REGEX_HEX_STRING = REGEX_HEX_STRING;
|
322
391
|
exports.addTrailingSlash = addTrailingSlash;
|
323
392
|
exports.blake2Concat = blake2Concat;
|
324
393
|
exports.classNames = classNames;
|
@@ -329,16 +398,20 @@ exports.encodeAnyAddress = encodeAnyAddress;
|
|
329
398
|
exports.firstThenDebounce = firstThenDebounce;
|
330
399
|
exports.formatDecimals = formatDecimals;
|
331
400
|
exports.formatPrice = formatPrice;
|
401
|
+
exports.getSharedObservable = getSharedObservable;
|
332
402
|
exports.hasOwnProperty = hasOwnProperty;
|
403
|
+
exports.isAbortError = isAbortError;
|
333
404
|
exports.isAddressEqual = isAddressEqual;
|
334
405
|
exports.isArrayOf = isArrayOf;
|
335
406
|
exports.isAscii = isAscii;
|
336
407
|
exports.isBigInt = isBigInt;
|
337
408
|
exports.isBooleanTrue = isBooleanTrue;
|
338
409
|
exports.isEthereumAddress = isEthereumAddress;
|
410
|
+
exports.isHexString = isHexString;
|
339
411
|
exports.isNotNil = isNotNil;
|
340
412
|
exports.isTruthy = isTruthy;
|
341
413
|
exports.isValidSubstrateAddress = isValidSubstrateAddress;
|
414
|
+
exports.keepAlive = keepAlive;
|
342
415
|
exports.normalizeAddress = normalizeAddress;
|
343
416
|
exports.planckToTokens = planckToTokens;
|
344
417
|
exports.sleep = sleep;
|
package/dist/talismn-util.esm.js
CHANGED
@@ -2,7 +2,7 @@ import { u8aToHex, u8aConcat, u8aToU8a, hexToU8a } from '@polkadot/util';
|
|
2
2
|
import { blake2AsU8a, isEthereumAddress as isEthereumAddress$1, ethereumEncode, decodeAddress as decodeAddress$1, base58Decode, checkAddressChecksum, xxhashAsU8a } from '@polkadot/util-crypto';
|
3
3
|
import { twMerge } from 'tailwind-merge';
|
4
4
|
import { encodeAddress, decodeAddress } from '@polkadot/keyring';
|
5
|
-
import { concat, take, skip, debounceTime } from 'rxjs';
|
5
|
+
import { concat, take, skip, debounceTime, shareReplay, tap } from 'rxjs';
|
6
6
|
import BigNumber from 'bignumber.js';
|
7
7
|
|
8
8
|
const addTrailingSlash = url => {
|
@@ -276,13 +276,13 @@ const validateHexString = str => {
|
|
276
276
|
throw new Error("Expected a hex string");
|
277
277
|
};
|
278
278
|
|
279
|
-
const CACHE = new Map();
|
279
|
+
const CACHE$1 = new Map();
|
280
280
|
|
281
281
|
// Normalize an address in a way that it can be compared to other addresses that have also been normalized
|
282
282
|
const normalizeAddress = address => {
|
283
283
|
try {
|
284
|
-
if (!CACHE.has(address)) CACHE.set(address, encodeAnyAddress(address));
|
285
|
-
return CACHE.get(address);
|
284
|
+
if (!CACHE$1.has(address)) CACHE$1.set(address, encodeAnyAddress(address));
|
285
|
+
return CACHE$1.get(address);
|
286
286
|
} catch (cause) {
|
287
287
|
throw new Error(`Unable to normalize address: ${address}`, {
|
288
288
|
cause
|
@@ -310,4 +310,72 @@ const isNotNil = value => value !== null && value !== undefined;
|
|
310
310
|
|
311
311
|
const isTruthy = value => Boolean(value);
|
312
312
|
|
313
|
-
|
313
|
+
const isAbortError = error => {
|
314
|
+
return error instanceof Error && error.name === "AbortError";
|
315
|
+
};
|
316
|
+
|
317
|
+
const CACHE = new Map();
|
318
|
+
|
319
|
+
/**
|
320
|
+
* When using react-rxjs hooks and state observables, the options are used as weak map keys.
|
321
|
+
* This means that if the options object is recreated on each render, the observable will be recreated as well.
|
322
|
+
* 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
|
323
|
+
*
|
324
|
+
* @param namespace
|
325
|
+
* @param args
|
326
|
+
* @param createObservable
|
327
|
+
* @param serializer
|
328
|
+
* @returns
|
329
|
+
*/
|
330
|
+
const getSharedObservable = (namespace, args, createObservable, serializer = args => JSON.stringify(args)) => {
|
331
|
+
const cacheKey = `${namespace}:${serializer(args)}`;
|
332
|
+
if (CACHE.has(cacheKey)) return CACHE.get(cacheKey);
|
333
|
+
const obs = createObservable(args);
|
334
|
+
const sharedObs = obs.pipe(shareReplay({
|
335
|
+
bufferSize: 1,
|
336
|
+
refCount: true
|
337
|
+
}));
|
338
|
+
CACHE.set(cacheKey, sharedObs);
|
339
|
+
return sharedObs;
|
340
|
+
};
|
341
|
+
|
342
|
+
/**
|
343
|
+
* An RxJS operator that keeps the source observable alive for a specified duration
|
344
|
+
* after all subscribers have unsubscribed. This prevents expensive re-subscriptions
|
345
|
+
* when subscribers come and go frequently.
|
346
|
+
*
|
347
|
+
* @param keepAliveMs - Duration in milliseconds to keep the source alive after last unsubscription
|
348
|
+
* @returns MonoTypeOperatorFunction that can be used in pipe()
|
349
|
+
*
|
350
|
+
* @example
|
351
|
+
* ```typescript
|
352
|
+
* const data$ = expensive_api_call$.pipe(
|
353
|
+
* keepAlive(3000) // Keep alive for 3 seconds
|
354
|
+
* );
|
355
|
+
* ```
|
356
|
+
*/
|
357
|
+
const keepAlive = timeout => {
|
358
|
+
let release;
|
359
|
+
return source => source.pipe(tap({
|
360
|
+
subscribe: () => {
|
361
|
+
release = keepSourceSubscribed(source, timeout);
|
362
|
+
},
|
363
|
+
unsubscribe: () => {
|
364
|
+
release?.();
|
365
|
+
}
|
366
|
+
}), shareReplay({
|
367
|
+
refCount: true,
|
368
|
+
bufferSize: 1
|
369
|
+
}));
|
370
|
+
};
|
371
|
+
const keepSourceSubscribed = (observable, ms) => {
|
372
|
+
const sub = observable.subscribe();
|
373
|
+
return () => setTimeout(() => sub.unsubscribe(), ms);
|
374
|
+
};
|
375
|
+
|
376
|
+
const REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
|
377
|
+
const isHexString = value => {
|
378
|
+
return typeof value === "string" && REGEX_HEX_STRING.test(value);
|
379
|
+
};
|
380
|
+
|
381
|
+
export { BigMath, Deferred, MAX_DECIMALS_FORMAT, REGEX_HEX_STRING, addTrailingSlash, blake2Concat, classNames, convertAddress, decodeAnyAddress, decodeSs58Format, encodeAnyAddress, firstThenDebounce, formatDecimals, formatPrice, getSharedObservable, hasOwnProperty, isAbortError, isAddressEqual, isArrayOf, isAscii, isBigInt, isBooleanTrue, isEthereumAddress, isHexString, isNotNil, isTruthy, isValidSubstrateAddress, keepAlive, normalizeAddress, planckToTokens, sleep, throwAfter, tokensToPlanck, twox64Concat, validateHexString };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@talismn/util",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.5.0",
|
4
4
|
"author": "Talisman",
|
5
5
|
"homepage": "https://talisman.xyz",
|
6
6
|
"license": "GPL-3.0-or-later",
|
@@ -26,16 +26,16 @@
|
|
26
26
|
"tailwind-merge": "^2.5.4"
|
27
27
|
},
|
28
28
|
"devDependencies": {
|
29
|
-
"@polkadot/keyring": "13.
|
30
|
-
"@polkadot/util": "13.
|
31
|
-
"@polkadot/util-crypto": "13.
|
29
|
+
"@polkadot/keyring": "13.5.1",
|
30
|
+
"@polkadot/util": "13.5.1",
|
31
|
+
"@polkadot/util-crypto": "13.5.1",
|
32
32
|
"@types/jest": "^29.5.14",
|
33
33
|
"eslint": "^8.57.1",
|
34
34
|
"jest": "^29.7.0",
|
35
35
|
"ts-jest": "^29.2.5",
|
36
36
|
"typescript": "^5.6.3",
|
37
|
-
"@talismn/
|
38
|
-
"@talismn/
|
37
|
+
"@talismn/tsconfig": "0.0.2",
|
38
|
+
"@talismn/eslint-config": "0.0.3"
|
39
39
|
},
|
40
40
|
"peerDependencies": {
|
41
41
|
"@polkadot/keyring": "*",
|