@talismn/util 0.0.0-pr2075-20250708160640 → 0.0.0-pr2075-20250709134044

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.
@@ -30,3 +30,4 @@ export * from "./isNotNil";
30
30
  export * from "./isTruthy";
31
31
  export * from "./isAbortError";
32
32
  export * from "./getSharedObservable";
33
+ export * from "./keepAlive";
@@ -0,0 +1,17 @@
1
+ import type { MonoTypeOperatorFunction } 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>(keepAliveMs: number) => MonoTypeOperatorFunction<T>;
@@ -345,6 +345,88 @@ const getSharedObservable = (namespace, args, createObservable, serializer = arg
345
345
  return sharedObs;
346
346
  };
347
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 = keepAliveMs => {
364
+ return source => {
365
+ let refCount = 0;
366
+ let sourceSubscription = null;
367
+ let cleanupTimer = null;
368
+ let hasCompleted = false;
369
+ let hasErrored = false;
370
+ let error = null;
371
+ const cleanup = () => {
372
+ if (sourceSubscription) {
373
+ sourceSubscription.unsubscribe();
374
+ sourceSubscription = null;
375
+ }
376
+ cleanupTimer = null;
377
+ hasCompleted = false;
378
+ hasErrored = false;
379
+ error = null;
380
+ };
381
+ return new rxjs.Observable(subscriber => {
382
+ // Cancel any pending cleanup
383
+ if (cleanupTimer) {
384
+ clearTimeout(cleanupTimer);
385
+ cleanupTimer = null;
386
+ }
387
+ refCount++;
388
+
389
+ // Handle already completed/errored states
390
+ if (hasCompleted) {
391
+ subscriber.complete();
392
+ return () => {
393
+ refCount--;
394
+ };
395
+ }
396
+ if (hasErrored) {
397
+ subscriber.error(error);
398
+ return () => {
399
+ refCount--;
400
+ };
401
+ }
402
+
403
+ // Create source subscription if it doesn't exist
404
+ if (!sourceSubscription) {
405
+ sourceSubscription = source.subscribe({
406
+ next: value => {
407
+ subscriber.next(value);
408
+ },
409
+ error: err => {
410
+ hasErrored = true;
411
+ error = err;
412
+ subscriber.error(err);
413
+ },
414
+ complete: () => {
415
+ hasCompleted = true;
416
+ subscriber.complete();
417
+ }
418
+ });
419
+ }
420
+ return () => {
421
+ refCount--;
422
+ if (refCount === 0) {
423
+ cleanupTimer = setTimeout(cleanup, keepAliveMs);
424
+ }
425
+ };
426
+ });
427
+ };
428
+ };
429
+
348
430
  exports.BigMath = BigMath;
349
431
  exports.Deferred = Deferred;
350
432
  exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
@@ -370,6 +452,7 @@ exports.isEthereumAddress = isEthereumAddress;
370
452
  exports.isNotNil = isNotNil;
371
453
  exports.isTruthy = isTruthy;
372
454
  exports.isValidSubstrateAddress = isValidSubstrateAddress;
455
+ exports.keepAlive = keepAlive;
373
456
  exports.normalizeAddress = normalizeAddress;
374
457
  exports.planckToTokens = planckToTokens;
375
458
  exports.sleep = sleep;
@@ -345,6 +345,88 @@ const getSharedObservable = (namespace, args, createObservable, serializer = arg
345
345
  return sharedObs;
346
346
  };
347
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 = keepAliveMs => {
364
+ return source => {
365
+ let refCount = 0;
366
+ let sourceSubscription = null;
367
+ let cleanupTimer = null;
368
+ let hasCompleted = false;
369
+ let hasErrored = false;
370
+ let error = null;
371
+ const cleanup = () => {
372
+ if (sourceSubscription) {
373
+ sourceSubscription.unsubscribe();
374
+ sourceSubscription = null;
375
+ }
376
+ cleanupTimer = null;
377
+ hasCompleted = false;
378
+ hasErrored = false;
379
+ error = null;
380
+ };
381
+ return new rxjs.Observable(subscriber => {
382
+ // Cancel any pending cleanup
383
+ if (cleanupTimer) {
384
+ clearTimeout(cleanupTimer);
385
+ cleanupTimer = null;
386
+ }
387
+ refCount++;
388
+
389
+ // Handle already completed/errored states
390
+ if (hasCompleted) {
391
+ subscriber.complete();
392
+ return () => {
393
+ refCount--;
394
+ };
395
+ }
396
+ if (hasErrored) {
397
+ subscriber.error(error);
398
+ return () => {
399
+ refCount--;
400
+ };
401
+ }
402
+
403
+ // Create source subscription if it doesn't exist
404
+ if (!sourceSubscription) {
405
+ sourceSubscription = source.subscribe({
406
+ next: value => {
407
+ subscriber.next(value);
408
+ },
409
+ error: err => {
410
+ hasErrored = true;
411
+ error = err;
412
+ subscriber.error(err);
413
+ },
414
+ complete: () => {
415
+ hasCompleted = true;
416
+ subscriber.complete();
417
+ }
418
+ });
419
+ }
420
+ return () => {
421
+ refCount--;
422
+ if (refCount === 0) {
423
+ cleanupTimer = setTimeout(cleanup, keepAliveMs);
424
+ }
425
+ };
426
+ });
427
+ };
428
+ };
429
+
348
430
  exports.BigMath = BigMath;
349
431
  exports.Deferred = Deferred;
350
432
  exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
@@ -370,6 +452,7 @@ exports.isEthereumAddress = isEthereumAddress;
370
452
  exports.isNotNil = isNotNil;
371
453
  exports.isTruthy = isTruthy;
372
454
  exports.isValidSubstrateAddress = isValidSubstrateAddress;
455
+ exports.keepAlive = keepAlive;
373
456
  exports.normalizeAddress = normalizeAddress;
374
457
  exports.planckToTokens = planckToTokens;
375
458
  exports.sleep = sleep;
@@ -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, shareReplay } from 'rxjs';
5
+ import { concat, take, skip, debounceTime, shareReplay, Observable } from 'rxjs';
6
6
  import BigNumber from 'bignumber.js';
7
7
 
8
8
  const addTrailingSlash = url => {
@@ -339,4 +339,86 @@ const getSharedObservable = (namespace, args, createObservable, serializer = arg
339
339
  return sharedObs;
340
340
  };
341
341
 
342
- export { BigMath, Deferred, MAX_DECIMALS_FORMAT, addTrailingSlash, blake2Concat, classNames, convertAddress, decodeAnyAddress, decodeSs58Format, encodeAnyAddress, firstThenDebounce, formatDecimals, formatPrice, getSharedObservable, hasOwnProperty, isAbortError, isAddressEqual, isArrayOf, isAscii, isBigInt, isBooleanTrue, isEthereumAddress, isNotNil, isTruthy, isValidSubstrateAddress, normalizeAddress, planckToTokens, sleep, throwAfter, tokensToPlanck, twox64Concat, validateHexString };
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 = keepAliveMs => {
358
+ return source => {
359
+ let refCount = 0;
360
+ let sourceSubscription = null;
361
+ let cleanupTimer = null;
362
+ let hasCompleted = false;
363
+ let hasErrored = false;
364
+ let error = null;
365
+ const cleanup = () => {
366
+ if (sourceSubscription) {
367
+ sourceSubscription.unsubscribe();
368
+ sourceSubscription = null;
369
+ }
370
+ cleanupTimer = null;
371
+ hasCompleted = false;
372
+ hasErrored = false;
373
+ error = null;
374
+ };
375
+ return new Observable(subscriber => {
376
+ // Cancel any pending cleanup
377
+ if (cleanupTimer) {
378
+ clearTimeout(cleanupTimer);
379
+ cleanupTimer = null;
380
+ }
381
+ refCount++;
382
+
383
+ // Handle already completed/errored states
384
+ if (hasCompleted) {
385
+ subscriber.complete();
386
+ return () => {
387
+ refCount--;
388
+ };
389
+ }
390
+ if (hasErrored) {
391
+ subscriber.error(error);
392
+ return () => {
393
+ refCount--;
394
+ };
395
+ }
396
+
397
+ // Create source subscription if it doesn't exist
398
+ if (!sourceSubscription) {
399
+ sourceSubscription = source.subscribe({
400
+ next: value => {
401
+ subscriber.next(value);
402
+ },
403
+ error: err => {
404
+ hasErrored = true;
405
+ error = err;
406
+ subscriber.error(err);
407
+ },
408
+ complete: () => {
409
+ hasCompleted = true;
410
+ subscriber.complete();
411
+ }
412
+ });
413
+ }
414
+ return () => {
415
+ refCount--;
416
+ if (refCount === 0) {
417
+ cleanupTimer = setTimeout(cleanup, keepAliveMs);
418
+ }
419
+ };
420
+ });
421
+ };
422
+ };
423
+
424
+ export { BigMath, Deferred, MAX_DECIMALS_FORMAT, addTrailingSlash, blake2Concat, classNames, convertAddress, decodeAnyAddress, decodeSs58Format, encodeAnyAddress, firstThenDebounce, formatDecimals, formatPrice, getSharedObservable, hasOwnProperty, isAbortError, isAddressEqual, isArrayOf, isAscii, isBigInt, isBooleanTrue, isEthereumAddress, 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.0.0-pr2075-20250708160640",
3
+ "version": "0.0.0-pr2075-20250709134044",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "GPL-3.0-or-later",