@talismn/util 0.5.2 → 0.5.5

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.
@@ -0,0 +1,45 @@
1
+ import { Observable } from "rxjs";
2
+ export type QueryStatus = "loading" | "loaded" | "error";
3
+ export type QueryResult<T, S extends QueryStatus = "loading" | "loaded" | "error"> = S extends "loading" ? {
4
+ status: "loading";
5
+ data: T | undefined;
6
+ error: undefined;
7
+ } : S extends "loaded" ? {
8
+ status: "loaded";
9
+ data: T;
10
+ error: undefined;
11
+ } : {
12
+ status: "error";
13
+ data: undefined;
14
+ error: unknown;
15
+ };
16
+ type QueryOptions<Output, Args> = {
17
+ namespace: string;
18
+ args: Args;
19
+ queryFn: (args: Args, signal: AbortSignal) => Promise<Output>;
20
+ defaultValue?: Output;
21
+ refreshInterval?: number;
22
+ serializer?: (args: Args) => string;
23
+ };
24
+ /**
25
+ * Creates a shared observable for executing queries with caching, loading states, and automatic refresh capabilities.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const userQuery$ = getQuery$({
30
+ * namespace: 'users',
31
+ * args: { userId: 123 },
32
+ * queryFn: async ({ userId }) => fetchUser(userId),
33
+ * defaultValue: null,
34
+ * refreshInterval: 30000
35
+ * });
36
+ *
37
+ * userQuery$.subscribe(result => {
38
+ * if (result.status === 'loaded') {
39
+ * console.log(result.data);
40
+ * }
41
+ * });
42
+ * ```
43
+ */
44
+ export declare const getQuery$: <Output, Args>({ namespace, args, queryFn, defaultValue, refreshInterval, serializer, }: QueryOptions<Output, Args>) => Observable<QueryResult<Output>>;
45
+ export {};
@@ -28,3 +28,4 @@ export * from "./throwAfter";
28
28
  export * from "./tokensToPlanck";
29
29
  export * from "./validateHexString";
30
30
  export * from "./getLoadable";
31
+ export * from "./getQuery";
@@ -3,6 +3,7 @@
3
3
  var tailwindMerge = require('tailwind-merge');
4
4
  var rxjs = require('rxjs');
5
5
  var BigNumber = require('bignumber.js');
6
+ var lodashEs = require('lodash-es');
6
7
 
7
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
9
 
@@ -387,6 +388,82 @@ const getGenericErrorMessage = error => {
387
388
  return String(error) || "Unknown error";
388
389
  };
389
390
 
391
+ /**
392
+ * Creates a shared observable for executing queries with caching, loading states, and automatic refresh capabilities.
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * const userQuery$ = getQuery$({
397
+ * namespace: 'users',
398
+ * args: { userId: 123 },
399
+ * queryFn: async ({ userId }) => fetchUser(userId),
400
+ * defaultValue: null,
401
+ * refreshInterval: 30000
402
+ * });
403
+ *
404
+ * userQuery$.subscribe(result => {
405
+ * if (result.status === 'loaded') {
406
+ * console.log(result.data);
407
+ * }
408
+ * });
409
+ * ```
410
+ */
411
+ const getQuery$ = ({
412
+ namespace,
413
+ args,
414
+ queryFn,
415
+ defaultValue,
416
+ refreshInterval,
417
+ serializer = args => JSON.stringify(args)
418
+ }) => {
419
+ return getSharedObservable(namespace, args, () => new rxjs.Observable(subscriber => {
420
+ const controller = new AbortController();
421
+ const result = new rxjs.BehaviorSubject({
422
+ status: "loading",
423
+ data: defaultValue,
424
+ error: undefined
425
+ });
426
+
427
+ // result subscription
428
+ const sub = result.pipe(rxjs.distinctUntilChanged(lodashEs.isEqual)).subscribe(subscriber);
429
+
430
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
431
+ let timeout = null;
432
+
433
+ // fetch result subscription
434
+ const run = () => {
435
+ if (controller.signal.aborted) return;
436
+ queryFn(args, controller.signal).then(data => {
437
+ if (controller.signal.aborted) return;
438
+ result.next({
439
+ status: "loaded",
440
+ data,
441
+ error: undefined
442
+ });
443
+ }).catch(error => {
444
+ if (controller.signal.aborted) return;
445
+ result.next({
446
+ status: "error",
447
+ data: undefined,
448
+ error
449
+ });
450
+ }).finally(() => {
451
+ if (controller.signal.aborted) return;
452
+ if (refreshInterval) timeout = setTimeout(run, refreshInterval);
453
+ });
454
+ };
455
+ run();
456
+ return () => {
457
+ sub.unsubscribe();
458
+ if (timeout) clearTimeout(timeout);
459
+ controller.abort(new Error("getQuery$ unsubscribed"));
460
+ };
461
+ }).pipe(rxjs.shareReplay({
462
+ refCount: true,
463
+ bufferSize: 1
464
+ })), serializer);
465
+ };
466
+
390
467
  exports.BigMath = BigMath;
391
468
  exports.Deferred = Deferred;
392
469
  exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
@@ -398,6 +475,7 @@ exports.firstThenDebounce = firstThenDebounce;
398
475
  exports.formatDecimals = formatDecimals;
399
476
  exports.formatPrice = formatPrice;
400
477
  exports.getLoadable$ = getLoadable$;
478
+ exports.getQuery$ = getQuery$;
401
479
  exports.getSharedObservable = getSharedObservable;
402
480
  exports.hasOwnProperty = hasOwnProperty;
403
481
  exports.isAbortError = isAbortError;
@@ -3,6 +3,7 @@
3
3
  var tailwindMerge = require('tailwind-merge');
4
4
  var rxjs = require('rxjs');
5
5
  var BigNumber = require('bignumber.js');
6
+ var lodashEs = require('lodash-es');
6
7
 
7
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
9
 
@@ -387,6 +388,82 @@ const getGenericErrorMessage = error => {
387
388
  return String(error) || "Unknown error";
388
389
  };
389
390
 
391
+ /**
392
+ * Creates a shared observable for executing queries with caching, loading states, and automatic refresh capabilities.
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * const userQuery$ = getQuery$({
397
+ * namespace: 'users',
398
+ * args: { userId: 123 },
399
+ * queryFn: async ({ userId }) => fetchUser(userId),
400
+ * defaultValue: null,
401
+ * refreshInterval: 30000
402
+ * });
403
+ *
404
+ * userQuery$.subscribe(result => {
405
+ * if (result.status === 'loaded') {
406
+ * console.log(result.data);
407
+ * }
408
+ * });
409
+ * ```
410
+ */
411
+ const getQuery$ = ({
412
+ namespace,
413
+ args,
414
+ queryFn,
415
+ defaultValue,
416
+ refreshInterval,
417
+ serializer = args => JSON.stringify(args)
418
+ }) => {
419
+ return getSharedObservable(namespace, args, () => new rxjs.Observable(subscriber => {
420
+ const controller = new AbortController();
421
+ const result = new rxjs.BehaviorSubject({
422
+ status: "loading",
423
+ data: defaultValue,
424
+ error: undefined
425
+ });
426
+
427
+ // result subscription
428
+ const sub = result.pipe(rxjs.distinctUntilChanged(lodashEs.isEqual)).subscribe(subscriber);
429
+
430
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
431
+ let timeout = null;
432
+
433
+ // fetch result subscription
434
+ const run = () => {
435
+ if (controller.signal.aborted) return;
436
+ queryFn(args, controller.signal).then(data => {
437
+ if (controller.signal.aborted) return;
438
+ result.next({
439
+ status: "loaded",
440
+ data,
441
+ error: undefined
442
+ });
443
+ }).catch(error => {
444
+ if (controller.signal.aborted) return;
445
+ result.next({
446
+ status: "error",
447
+ data: undefined,
448
+ error
449
+ });
450
+ }).finally(() => {
451
+ if (controller.signal.aborted) return;
452
+ if (refreshInterval) timeout = setTimeout(run, refreshInterval);
453
+ });
454
+ };
455
+ run();
456
+ return () => {
457
+ sub.unsubscribe();
458
+ if (timeout) clearTimeout(timeout);
459
+ controller.abort(new Error("getQuery$ unsubscribed"));
460
+ };
461
+ }).pipe(rxjs.shareReplay({
462
+ refCount: true,
463
+ bufferSize: 1
464
+ })), serializer);
465
+ };
466
+
390
467
  exports.BigMath = BigMath;
391
468
  exports.Deferred = Deferred;
392
469
  exports.MAX_DECIMALS_FORMAT = MAX_DECIMALS_FORMAT;
@@ -398,6 +475,7 @@ exports.firstThenDebounce = firstThenDebounce;
398
475
  exports.formatDecimals = formatDecimals;
399
476
  exports.formatPrice = formatPrice;
400
477
  exports.getLoadable$ = getLoadable$;
478
+ exports.getQuery$ = getQuery$;
401
479
  exports.getSharedObservable = getSharedObservable;
402
480
  exports.hasOwnProperty = hasOwnProperty;
403
481
  exports.isAbortError = isAbortError;
@@ -1,6 +1,7 @@
1
1
  import { twMerge } from 'tailwind-merge';
2
- import { concat, take, skip, debounceTime, shareReplay, Subject, tap, ReplaySubject, timer, switchMap, from, startWith, map, catchError, of } from 'rxjs';
2
+ import { concat, take, skip, debounceTime, shareReplay, Subject, tap, ReplaySubject, timer, switchMap, from, startWith, map, catchError, of, Observable, BehaviorSubject, distinctUntilChanged } from 'rxjs';
3
3
  import BigNumber from 'bignumber.js';
4
+ import { isEqual } from 'lodash-es';
4
5
 
5
6
  const addTrailingSlash = url => {
6
7
  if (url.endsWith("/")) {
@@ -381,4 +382,80 @@ const getGenericErrorMessage = error => {
381
382
  return String(error) || "Unknown error";
382
383
  };
383
384
 
384
- export { BigMath, Deferred, MAX_DECIMALS_FORMAT, REGEX_HEX_STRING, addTrailingSlash, classNames, cn, firstThenDebounce, formatDecimals, formatPrice, getLoadable$, getSharedObservable, hasOwnProperty, isAbortError, isArrayOf, isAscii, isBigInt, isBooleanTrue, isHexString, isNotNil, isPromise, isSubject, isTruthy, keepAlive, planckToTokens, replaySubjectFrom, sleep, splitSubject, throwAfter, tokensToPlanck, validateHexString };
385
+ /**
386
+ * Creates a shared observable for executing queries with caching, loading states, and automatic refresh capabilities.
387
+ *
388
+ * @example
389
+ * ```typescript
390
+ * const userQuery$ = getQuery$({
391
+ * namespace: 'users',
392
+ * args: { userId: 123 },
393
+ * queryFn: async ({ userId }) => fetchUser(userId),
394
+ * defaultValue: null,
395
+ * refreshInterval: 30000
396
+ * });
397
+ *
398
+ * userQuery$.subscribe(result => {
399
+ * if (result.status === 'loaded') {
400
+ * console.log(result.data);
401
+ * }
402
+ * });
403
+ * ```
404
+ */
405
+ const getQuery$ = ({
406
+ namespace,
407
+ args,
408
+ queryFn,
409
+ defaultValue,
410
+ refreshInterval,
411
+ serializer = args => JSON.stringify(args)
412
+ }) => {
413
+ return getSharedObservable(namespace, args, () => new Observable(subscriber => {
414
+ const controller = new AbortController();
415
+ const result = new BehaviorSubject({
416
+ status: "loading",
417
+ data: defaultValue,
418
+ error: undefined
419
+ });
420
+
421
+ // result subscription
422
+ const sub = result.pipe(distinctUntilChanged(isEqual)).subscribe(subscriber);
423
+
424
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
425
+ let timeout = null;
426
+
427
+ // fetch result subscription
428
+ const run = () => {
429
+ if (controller.signal.aborted) return;
430
+ queryFn(args, controller.signal).then(data => {
431
+ if (controller.signal.aborted) return;
432
+ result.next({
433
+ status: "loaded",
434
+ data,
435
+ error: undefined
436
+ });
437
+ }).catch(error => {
438
+ if (controller.signal.aborted) return;
439
+ result.next({
440
+ status: "error",
441
+ data: undefined,
442
+ error
443
+ });
444
+ }).finally(() => {
445
+ if (controller.signal.aborted) return;
446
+ if (refreshInterval) timeout = setTimeout(run, refreshInterval);
447
+ });
448
+ };
449
+ run();
450
+ return () => {
451
+ sub.unsubscribe();
452
+ if (timeout) clearTimeout(timeout);
453
+ controller.abort(new Error("getQuery$ unsubscribed"));
454
+ };
455
+ }).pipe(shareReplay({
456
+ refCount: true,
457
+ bufferSize: 1
458
+ })), serializer);
459
+ };
460
+
461
+ export { BigMath, Deferred, MAX_DECIMALS_FORMAT, REGEX_HEX_STRING, addTrailingSlash, classNames, cn, firstThenDebounce, formatDecimals, formatPrice, getLoadable$, getQuery$, getSharedObservable, hasOwnProperty, isAbortError, isArrayOf, isAscii, isBigInt, isBooleanTrue, isHexString, isNotNil, isPromise, isSubject, isTruthy, keepAlive, planckToTokens, replaySubjectFrom, sleep, splitSubject, throwAfter, tokensToPlanck, validateHexString };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/util",
3
- "version": "0.5.2",
3
+ "version": "0.5.5",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "GPL-3.0-or-later",
@@ -23,16 +23,18 @@
23
23
  "dependencies": {
24
24
  "bignumber.js": "^9.1.2",
25
25
  "rxjs": "^7.8.1",
26
- "tailwind-merge": "^2.5.4"
26
+ "tailwind-merge": "^2.5.4",
27
+ "lodash-es": "4.17.21"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@types/jest": "^29.5.14",
31
+ "@types/lodash-es": "4.17.12",
30
32
  "eslint": "^8.57.1",
31
33
  "jest": "^29.7.0",
32
34
  "ts-jest": "^29.2.5",
33
35
  "typescript": "^5.6.3",
34
36
  "@talismn/eslint-config": "0.0.3",
35
- "@talismn/tsconfig": "0.0.2"
37
+ "@talismn/tsconfig": "0.0.3"
36
38
  },
37
39
  "peerDependencies": {
38
40
  "rxjs": ">= 7.8.1"