@cascateer/core 2.0.26 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cascateer/core",
3
- "version": "2.0.26",
3
+ "version": "2.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cascateer/core.git"
package/src/api.ts CHANGED
@@ -20,8 +20,8 @@ import {
20
20
  UnaryFunction,
21
21
  } from "rxjs";
22
22
  import { asObservable, ExtendableDictionary, property } from "./lib";
23
- import { TapObservable } from "./observable";
24
- import { Action, MaybeArray, MaybeObservable, TapEffect } from "./types";
23
+ import { AsyncObservable } from "./observable";
24
+ import { Action, AsyncEffect, MaybeArray, MaybeObservable } from "./types";
25
25
 
26
26
  interface TagsConstructor<Args, Result> {
27
27
  (args: Args, result: Result): string[];
@@ -36,7 +36,7 @@ class Memoizable<Args, Result> {
36
36
  predicate: UnaryFunction<Args, Observable<Result>>;
37
37
  tags: TagsConstructor<Args, Result>;
38
38
 
39
- subscribe: UnaryFunction<Observable<string[]>, TapEffect<Args, Result>>;
39
+ subscribe: UnaryFunction<Observable<string[]>, AsyncEffect<Args, Result>>;
40
40
 
41
41
  share: UnaryFunction<NextObserver<string[]>, Action<Args, Result>>;
42
42
 
@@ -46,9 +46,9 @@ class Memoizable<Args, Result> {
46
46
  this.tags = isFunction(tags) ? tags : constant([tags ?? []].flat());
47
47
 
48
48
  this.subscribe = (invalidatedTags) => {
49
- const memoizedEffect: TapEffect<Args, Result> = memoize(
49
+ const memoizedEffect: AsyncEffect<Args, Result> = memoize(
50
50
  (args) =>
51
- new TapObservable(
51
+ new AsyncObservable(
52
52
  this.predicate(args).pipe(
53
53
  repeat({
54
54
  delay: () =>
@@ -79,7 +79,7 @@ class Memoizable<Args, Result> {
79
79
  }
80
80
  }
81
81
 
82
- export interface ApiEffect<Args, Result> extends TapEffect<Args, Result> {}
82
+ export interface ApiEffect<Args, Result> extends AsyncEffect<Args, Result> {}
83
83
 
84
84
  type ApiAdapterPropertyConstructor<Source, Type extends "effect" | "action"> = {
85
85
  [T in Type]: <Args, Result>(
@@ -0,0 +1,20 @@
1
+ import { BehaviorSubject, defer, finalize, Observable } from "rxjs";
2
+ import { ProxyObservable } from "./ProxyObservable";
3
+
4
+ export interface AsyncObservable<T> {
5
+ pending: Observable<boolean>;
6
+ }
7
+
8
+ export class AsyncObservable<T> extends ProxyObservable<T> {
9
+ constructor(source: Observable<T>) {
10
+ const pending = new BehaviorSubject(false);
11
+
12
+ super(source, (source) =>
13
+ defer(() => (pending.next(true), source)).pipe(
14
+ finalize(() => pending.next(false)),
15
+ ),
16
+ );
17
+
18
+ this.pending = pending;
19
+ }
20
+ }
@@ -1,12 +1,15 @@
1
1
  import { once } from "lodash";
2
- import { Observable } from "rxjs";
2
+ import { identity, Observable } from "rxjs";
3
3
 
4
4
  export interface ProxyObservableHandler<T> {
5
5
  (target: Observable<T>, receiver: Observable<T>): Observable<T>;
6
6
  }
7
7
 
8
8
  export class ProxyObservable<T> extends Observable<T> {
9
- constructor(target: Observable<T>, handler: ProxyObservableHandler<T>) {
9
+ constructor(
10
+ target: Observable<T>,
11
+ handler: ProxyObservableHandler<T> = identity,
12
+ ) {
10
13
  handler = once(handler);
11
14
 
12
15
  super((subscriber) => handler(target, this).subscribe(subscriber));
@@ -1,6 +1,6 @@
1
1
  import { clone, identity, isEqual, memoize } from "lodash";
2
2
  import { distinctUntilChanged, map, Observable, of, UnaryFunction } from "rxjs";
3
- import { TapObservable } from ".";
3
+ import { AsyncObservable, ProxyObservable } from ".";
4
4
  import {
5
5
  asEnumerable,
6
6
  EnumerableItem,
@@ -33,8 +33,11 @@ class SignalReflector<T> {
33
33
  new SignalReflector((transform) => this.predicate(lift(transform)));
34
34
  }
35
35
 
36
- export class Signal<T> extends Observable<T> implements TapObservable<T> {
37
- loading = of(false);
36
+ export class Signal<T>
37
+ extends ProxyObservable<T>
38
+ implements AsyncObservable<T>
39
+ {
40
+ pending = of(false);
38
41
 
39
42
  clone(): Signal<T> {
40
43
  return this;
@@ -56,7 +59,7 @@ export class Signal<T> extends Observable<T> implements TapObservable<T> {
56
59
  enumerator?: SignalEnumerator<T>;
57
60
  reflector?: SignalReflector<T>;
58
61
  }) {
59
- super((subscriber) => value.subscribe(subscriber));
62
+ super(value);
60
63
 
61
64
  this.enumerator = enumerator;
62
65
  this.reflector = reflector;
@@ -1,5 +1,5 @@
1
+ export { AsyncObservable } from "./AsyncObservable";
1
2
  export { Future } from "./Future";
2
3
  export { ProxyObservable } from "./ProxyObservable";
3
4
  export { ProxySubject } from "./ProxySubject";
4
5
  export { ComputedSignal, Signal } from "./Signal";
5
- export { TapObservable } from "./TapObservable";
package/src/terminal.ts CHANGED
@@ -1,13 +1,21 @@
1
- import { Dictionary, memoize, thru } from "lodash";
2
- import objectHash from "object-hash";
1
+ import { Dictionary, thru } from "lodash";
3
2
  import { UnaryFunction } from "rxjs";
4
3
  import { ApiAdapter, ApiEffect } from "./api";
5
4
  import { ExtendableDictionary } from "./lib";
6
- import { ComputedSignal, TapObservable } from "./observable";
5
+ import { ComputedSignal } from "./observable";
7
6
  import { asStoreEffects, StoreAdapter, StoreEffects } from "./store";
8
- import { Action, Effect, TapEffect, TapEffects } from "./types";
7
+ import {
8
+ Action,
9
+ AsyncEffect,
10
+ AsyncEffectInterceptor,
11
+ AsyncEffects,
12
+ Effect,
13
+ } from "./types";
9
14
 
10
- export interface TerminalEffect<Args, Result> extends TapEffect<Args, Result> {}
15
+ export interface TerminalEffect<Args, Result> extends AsyncEffect<
16
+ Args,
17
+ Result
18
+ > {}
11
19
 
12
20
  export class TerminalAdapter<
13
21
  Effects extends Dictionary<TerminalEffect<any, any>>,
@@ -56,10 +64,10 @@ export class ExtendableTerminalAdapter<
56
64
  effects: StoreEffects<StoreSignals>;
57
65
  };
58
66
  api: {
59
- effects: TapEffects<ApiEffects>;
67
+ effects: AsyncEffects<ApiEffects>;
60
68
  };
61
69
  terminal: {
62
- effects: TapEffects<Effects>;
70
+ effects: AsyncEffects<Effects>;
63
71
  };
64
72
  },
65
73
  Effect<Args, Result>
@@ -75,23 +83,21 @@ export class ExtendableTerminalAdapter<
75
83
  (currentEffects) => () =>
76
84
  effects({
77
85
  effect: (constructor) => {
86
+ const interceptor = new AsyncEffectInterceptor();
87
+
78
88
  return thru(
79
89
  constructor({
80
90
  store: {
81
91
  effects: asStoreEffects(this.context.store.signals),
82
92
  },
83
93
  api: {
84
- effects: this.context.api.effects,
94
+ effects: interceptor.intercept(this.context.api.effects),
85
95
  },
86
96
  terminal: {
87
- effects: currentEffects,
97
+ effects: interceptor.intercept(currentEffects),
88
98
  },
89
99
  }),
90
- (effect) =>
91
- memoize(
92
- (args) => new TapObservable(effect(args)),
93
- (args) => objectHash(args ?? null),
94
- ),
100
+ interceptor.toAsyncEffect,
95
101
  );
96
102
  },
97
103
  }),
package/src/types.ts CHANGED
@@ -1,32 +1,75 @@
1
- import { Dictionary } from "lodash";
2
- import { UnaryFunction } from "rxjs";
1
+ import { Dictionary, mapValues, tap } from "lodash";
2
+ import {
3
+ combineLatest,
4
+ distinct,
5
+ map,
6
+ ReplaySubject,
7
+ switchMap,
8
+ UnaryFunction,
9
+ } from "rxjs";
3
10
  import { Observable } from "rxjs/internal/Observable";
4
11
  import { ObservableInput } from "rxjs/internal/types";
5
- import { TapObservable } from "./observable";
12
+ import { AsyncObservable, ProxyObservable } from "./observable";
13
+ import { concat } from "./operators";
6
14
 
7
15
  export interface Effect<Args, Result> extends UnaryFunction<
8
16
  Args,
9
17
  Observable<Result>
10
18
  > {}
11
19
 
12
- export interface TapEffect<Args, Result> extends UnaryFunction<
20
+ export interface AsyncEffect<Args, Result> extends UnaryFunction<
13
21
  Args,
14
- TapObservable<Result>
22
+ AsyncObservable<Result>
15
23
  > {}
16
24
 
17
- export type TapEffects<Effects extends Dictionary<TapEffect<any, any>>> = {
25
+ export type AsyncEffects<Effects extends Dictionary<AsyncEffect<any, any>>> = {
18
26
  [K in keyof Effects]: ReturnType<
19
27
  <
20
- Args extends Effects[K] extends TapEffect<infer Args, infer _>
28
+ Args extends Effects[K] extends AsyncEffect<infer Args, infer _>
21
29
  ? Args
22
30
  : never,
23
- Result extends Effects[K] extends TapEffect<infer _, infer Result>
31
+ Result extends Effects[K] extends AsyncEffect<infer _, infer Result>
24
32
  ? Result
25
33
  : never,
26
- >() => TapEffect<Args, Result>
34
+ >() => AsyncEffect<Args, Result>
27
35
  >;
28
36
  };
29
37
 
38
+ export class AsyncEffectInterceptor extends ReplaySubject<
39
+ AsyncObservable<any>
40
+ > {
41
+ intercept = <Effects extends Dictionary<AsyncEffect<any, any>>>(
42
+ effects: Effects,
43
+ ): AsyncEffects<Effects> =>
44
+ mapValues(
45
+ effects,
46
+ (effect) => (args) => tap(effect(args), (source) => this.next(source)),
47
+ );
48
+
49
+ toAsyncEffect =
50
+ <Args, Result>(effect: Effect<Args, Result>): AsyncEffect<Args, Result> =>
51
+ (args) =>
52
+ new (class
53
+ extends ProxyObservable<Result>
54
+ implements AsyncObservable<Result>
55
+ {
56
+ pending: Observable<boolean>;
57
+
58
+ constructor(interceptor: AsyncEffectInterceptor) {
59
+ super(effect(args));
60
+
61
+ this.pending = interceptor.pipe(
62
+ distinct(),
63
+ concat(),
64
+ switchMap((sources) =>
65
+ combineLatest(sources.map((source) => source.pending)),
66
+ ),
67
+ map((values) => values.some(Boolean)),
68
+ );
69
+ }
70
+ })(this);
71
+ }
72
+
30
73
  export interface Action<Args, Result> extends UnaryFunction<
31
74
  Args,
32
75
  Promise<Result>
@@ -1,20 +0,0 @@
1
- import { BehaviorSubject, defer, finalize, Observable } from "rxjs";
2
- import { ProxyObservable } from "./ProxyObservable";
3
-
4
- export interface TapObservable<T> {
5
- loading: Observable<boolean>;
6
- }
7
-
8
- export class TapObservable<T> extends ProxyObservable<T> {
9
- constructor(source: Observable<T>) {
10
- const loading = new BehaviorSubject(false);
11
-
12
- super(source, (source) =>
13
- defer(() => (loading.next(true), source)).pipe(
14
- finalize(() => loading.next(false)),
15
- ),
16
- );
17
-
18
- this.loading = loading;
19
- }
20
- }