@cascateer/core 2.1.14 → 2.1.16

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