@fozy-labs/rx-toolkit 0.4.1 → 0.4.2
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/README.md +41 -35
- package/dist/devtools/combineDevtools.d.ts +2 -0
- package/dist/devtools/combineDevtools.js +10 -0
- package/dist/devtools/index.d.ts +2 -0
- package/dist/devtools/index.js +2 -0
- package/dist/devtools/reduxDevtools.d.ts +2 -0
- package/dist/devtools/reduxDevtools.js +24 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/query/api/DefaultOptions.d.ts +2 -2
- package/dist/query/core/Opertation/Operation.js +5 -4
- package/dist/query/core/Opertation/OperationAgent.d.ts +1 -1
- package/dist/query/core/Opertation/OperationAgent.js +3 -3
- package/dist/query/core/QueriesCache.js +11 -5
- package/dist/query/core/Resource/Resource.d.ts +14 -2
- package/dist/query/core/Resource/Resource.js +34 -2
- package/dist/query/core/Resource/ResourceAgent.d.ts +1 -1
- package/dist/query/core/Resource/ResourceAgent.js +3 -3
- package/dist/query/core/Resource/ResourceRef.d.ts +2 -1
- package/dist/query/core/Resource/ResourceRef.js +96 -2
- package/dist/query/core/SharedOptions.d.ts +2 -2
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/lib/ReactiveCache.js +2 -2
- package/dist/query/react/useResourceRef.d.ts +6 -0
- package/dist/query/react/useResourceRef.js +8 -0
- package/dist/query/types/Operation.types.d.ts +43 -2
- package/dist/query/types/Resource.types.d.ts +16 -1
- package/dist/query/types/devtools.d.ts +6 -0
- package/dist/react-hooks/useSignal.d.ts +1 -1
- package/dist/signals/base/Batcher.d.ts +6 -0
- package/dist/signals/base/Batcher.js +55 -0
- package/dist/{signal → signals}/base/Computed.d.ts +5 -2
- package/dist/{signal → signals}/base/Computed.js +7 -3
- package/dist/{signal → signals}/base/Effect.d.ts +3 -3
- package/dist/signals/base/Effect.js +51 -0
- package/dist/signals/base/Indexer.d.ts +4 -0
- package/dist/signals/base/Indexer.js +6 -0
- package/dist/{signal → signals}/base/ReadonlySignal.d.ts +2 -0
- package/dist/signals/base/ReadonlySignal.js +30 -0
- package/dist/{signal → signals}/base/Signal.d.ts +12 -1
- package/dist/{signal → signals}/base/Signal.js +23 -3
- package/dist/signals/base/Tracker.d.ts +14 -0
- package/dist/signals/base/Tracker.js +13 -0
- package/dist/{signal → signals}/base/types.d.ts +0 -1
- package/dist/signals/base/types.js +1 -0
- package/dist/{signal → signals}/extends/LocalSignal.d.ts +1 -1
- package/dist/{signal → signals}/extends/LocalSignal.js +3 -3
- package/dist/{signal → signals}/operators/filterUpdates.js +1 -1
- package/dist/signals/operators/index.d.ts +3 -0
- package/dist/signals/operators/index.js +3 -0
- package/dist/{signal → signals}/operators/mapSignals.js +1 -1
- package/dist/{signal → signals}/operators/signalize.d.ts +1 -1
- package/dist/{signal → signals}/operators/signalize.js +1 -1
- package/docs/devtools/README.md +95 -0
- package/docs/query/README.md +25 -14
- package/docs/signals/README.md +10 -7
- package/package.json +13 -8
- package/dist/query/api/createDevtools.d.ts +0 -1
- package/dist/query/api/createDevtools.js +0 -6
- package/dist/signal/base/Batcher.d.ts +0 -7
- package/dist/signal/base/Batcher.js +0 -21
- package/dist/signal/base/Effect.js +0 -69
- package/dist/signal/base/ReadonlySignal.js +0 -20
- package/dist/signal/base/Tracker.d.ts +0 -5
- package/dist/signal/base/Tracker.js +0 -7
- package/dist/signal/operators/batch.d.ts +0 -2
- package/dist/signal/operators/batch.js +0 -27
- package/dist/signal/operators/index.d.ts +0 -4
- package/dist/signal/operators/index.js +0 -4
- /package/dist/{signal/base/types.js → query/types/devtools.js} +0 -0
- /package/dist/{signal → signals}/base/SyncObservable.d.ts +0 -0
- /package/dist/{signal → signals}/base/SyncObservable.js +0 -0
- /package/dist/{signal → signals}/base/index.d.ts +0 -0
- /package/dist/{signal → signals}/base/index.js +0 -0
- /package/dist/{signal → signals}/extends/index.d.ts +0 -0
- /package/dist/{signal → signals}/extends/index.js +0 -0
- /package/dist/{signal → signals}/index.d.ts +0 -0
- /package/dist/{signal → signals}/index.js +0 -0
- /package/dist/{signal → signals}/operators/filterUpdates.d.ts +0 -0
- /package/dist/{signal → signals}/operators/mapSignals.d.ts +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReadableSignalLike } from "../../
|
|
1
|
+
import { ReadableSignalLike } from "../../signals";
|
|
2
2
|
import { FallbackOnNever } from "../../query/types/shared.types";
|
|
3
3
|
import { ResourceDefinition, ResourceInstance } from "./Resource.types";
|
|
4
4
|
/**
|
|
@@ -20,21 +20,62 @@ export type OperationCreateOptions<D extends OperationDefinition> = {
|
|
|
20
20
|
* Настройки связи операции с ресурсом
|
|
21
21
|
*/
|
|
22
22
|
export type LinkOptions<D extends OperationDefinition, RD extends ResourceDefinition> = {
|
|
23
|
+
/**
|
|
24
|
+
* Целевой ресурс, с которым связывается операция
|
|
25
|
+
* @required
|
|
26
|
+
*/
|
|
23
27
|
resource: ResourceInstance<RD>;
|
|
28
|
+
/**
|
|
29
|
+
* Функция для получения аргументов ресурса из аргументов операции.
|
|
30
|
+
* Используется для определения какой именно элемент в кэше ресурса нужно обновить
|
|
31
|
+
* @required
|
|
32
|
+
*/
|
|
24
33
|
forwardArgs: (args: D["Args"]) => RD["Args"];
|
|
34
|
+
/**
|
|
35
|
+
* Флаг для инвалидации (очистки) кэша ресурса после выполнения операции.
|
|
36
|
+
* При true - кэш будет очищен и ресурс будет перезагружен при следующем обращении
|
|
37
|
+
* @optional @default false
|
|
38
|
+
*/
|
|
25
39
|
invalidate?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Флаг для блокировки ресурса во время выполнения операции.
|
|
42
|
+
* При true - ресурс будет заблокирован и не сможет выполнять новые запросы
|
|
43
|
+
* @optional @default false
|
|
44
|
+
*/
|
|
26
45
|
lock?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Функция для обновления кэша ресурса после успешного выполнения операции.
|
|
48
|
+
* Получает draft объект для мутации, аргументы операции и результат операции
|
|
49
|
+
* @optional
|
|
50
|
+
*/
|
|
27
51
|
update?: (tools: {
|
|
52
|
+
/** Immer draft объект для мутации кэша ресурса */
|
|
28
53
|
draft: RD["Data"];
|
|
54
|
+
/** Аргументы, с которыми была вызвана операция */
|
|
29
55
|
args: D["Args"];
|
|
56
|
+
/** Результат выполнения операции */
|
|
30
57
|
data: D["Data"];
|
|
31
58
|
}) => void | RD["Data"] | Promise<RD["Data"]>;
|
|
59
|
+
/**
|
|
60
|
+
* Функция для оптимистичного обновления кэша ресурса ДО выполнения операции.
|
|
61
|
+
* Позволяет обновить UI немедленно, до получения ответа от сервера
|
|
62
|
+
* @optional
|
|
63
|
+
*/
|
|
32
64
|
optimisticUpdate?: (tools: {
|
|
65
|
+
/** Immer draft объект для мутации кэша ресурса */
|
|
33
66
|
draft: RD["Data"];
|
|
67
|
+
/** Аргументы, с которыми была вызвана операция */
|
|
34
68
|
args: D["Args"];
|
|
35
|
-
}) => void | RD["Data"] | Promise<
|
|
69
|
+
}) => void | RD["Data"] | Promise<RD["Data"]>;
|
|
70
|
+
/**
|
|
71
|
+
* Функция для создания нового элемента в кэше ресурса.
|
|
72
|
+
* Используется когда операция создает новую сущность, которую нужно добавить в кэш
|
|
73
|
+
* @optional
|
|
74
|
+
*/
|
|
36
75
|
create?: (tools: {
|
|
76
|
+
/** Аргументы, с которыми была вызвана операция */
|
|
37
77
|
args: D["Args"];
|
|
78
|
+
/** Результат выполнения операции */
|
|
38
79
|
data: D["Data"];
|
|
39
80
|
}) => RD["Data"] | Promise<RD["Data"]>;
|
|
40
81
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { ReadableSignalLike } from "../../
|
|
1
|
+
import { ReadableSignalLike } from "../../signals";
|
|
2
2
|
import { FallbackOnNever } from "../../query/types/shared.types";
|
|
3
|
+
import { Patch as ImmerPatch } from "immer";
|
|
3
4
|
/**
|
|
4
5
|
* Функция создания ресурса
|
|
5
6
|
*/
|
|
@@ -72,6 +73,16 @@ export type ResourceQueryState<D extends ResourceDefinition> = {
|
|
|
72
73
|
/** Аргументы запроса */
|
|
73
74
|
args: D["Args"] | undefined;
|
|
74
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* Транзакция ресурса
|
|
78
|
+
*/
|
|
79
|
+
export type ResourceTransaction = {
|
|
80
|
+
patches: ImmerPatch[];
|
|
81
|
+
inversePatches: ImmerPatch[];
|
|
82
|
+
status: 'pending' | 'committed' | 'aborted';
|
|
83
|
+
abort(): void;
|
|
84
|
+
commit(): void;
|
|
85
|
+
};
|
|
75
86
|
/**
|
|
76
87
|
* Эте не ссылка в "классическом" понимании, а абстракция
|
|
77
88
|
* для работы с элементом кеша ресурса.
|
|
@@ -82,9 +93,13 @@ export type ResourceRefInstanse<D extends ResourceDefinition> = {
|
|
|
82
93
|
unlock: () => void;
|
|
83
94
|
};
|
|
84
95
|
unlockOne(): void;
|
|
96
|
+
/**
|
|
97
|
+
* @deprecated
|
|
98
|
+
*/
|
|
85
99
|
update(updateFn: (data: D['Data']) => D['Data']): {
|
|
86
100
|
rollback: () => void;
|
|
87
101
|
};
|
|
102
|
+
patch(patchFn: (data: D['Data']) => void): ResourceTransaction | null;
|
|
88
103
|
invalidate(): void;
|
|
89
104
|
create(data: D['Data']): void;
|
|
90
105
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ReadableSignalLike } from "../signals";
|
|
2
2
|
export declare function useSignal<T>(signal$: ReadableSignalLike<T>): T;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const Scheduled = {
|
|
2
|
+
map: new Map(),
|
|
3
|
+
lowestRang: -1,
|
|
4
|
+
isLocked: false,
|
|
5
|
+
set(rang, fn) {
|
|
6
|
+
if (rang < this.lowestRang)
|
|
7
|
+
this.lowestRang = rang;
|
|
8
|
+
if (!this.map.has(rang)) {
|
|
9
|
+
this.map.set(rang, new Set());
|
|
10
|
+
}
|
|
11
|
+
this.map.get(rang).add(fn);
|
|
12
|
+
},
|
|
13
|
+
done() {
|
|
14
|
+
this.lowestRang = -1;
|
|
15
|
+
this.map.clear();
|
|
16
|
+
},
|
|
17
|
+
handleInfinity() {
|
|
18
|
+
const fns = this.map.get(Infinity);
|
|
19
|
+
this.map.delete(Infinity);
|
|
20
|
+
fns?.forEach((fn) => fn());
|
|
21
|
+
this.done();
|
|
22
|
+
},
|
|
23
|
+
run() {
|
|
24
|
+
if (this.map.size === 1 && this.map.has(Infinity))
|
|
25
|
+
return this.handleInfinity();
|
|
26
|
+
if (this.map.size === 0)
|
|
27
|
+
return this.done();
|
|
28
|
+
const iterationRang = this.lowestRang;
|
|
29
|
+
this.lowestRang += 1;
|
|
30
|
+
const fns = this.map.get(iterationRang);
|
|
31
|
+
this.map.delete(iterationRang);
|
|
32
|
+
fns?.forEach((fn) => fn());
|
|
33
|
+
this.run();
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export const Batcher = {
|
|
37
|
+
scheduler(rang) {
|
|
38
|
+
return {
|
|
39
|
+
schedule: (fn) => {
|
|
40
|
+
if (!Scheduled.isLocked)
|
|
41
|
+
return fn();
|
|
42
|
+
Scheduled.set(rang, fn);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
batch(fn) {
|
|
47
|
+
if (Scheduled.isLocked)
|
|
48
|
+
return fn();
|
|
49
|
+
Scheduled.isLocked = true;
|
|
50
|
+
const v = fn();
|
|
51
|
+
Scheduled.run();
|
|
52
|
+
Scheduled.isLocked = false;
|
|
53
|
+
return v;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { SubscriptionLike } from "rxjs";
|
|
2
|
-
import { ReadableSignalLike } from "
|
|
2
|
+
import { ReadableSignalLike } from "./types";
|
|
3
3
|
import { Signal } from "./Signal";
|
|
4
4
|
export declare class Computed<T> extends Signal<T> implements SubscriptionLike, ReadableSignalLike<T> {
|
|
5
5
|
private static _EMPTY;
|
|
6
6
|
private _effect;
|
|
7
|
-
constructor(computeFn: () => T,
|
|
7
|
+
constructor(computeFn: () => T, options?: {
|
|
8
|
+
disableDevtools?: boolean;
|
|
9
|
+
devtoolsName?: string;
|
|
10
|
+
});
|
|
8
11
|
unsubscribe(): void;
|
|
9
12
|
complete(): void;
|
|
10
13
|
}
|
|
@@ -3,16 +3,20 @@ import { Effect } from "./Effect";
|
|
|
3
3
|
export class Computed extends Signal {
|
|
4
4
|
static _EMPTY = Symbol('empty');
|
|
5
5
|
_effect;
|
|
6
|
-
constructor(computeFn,
|
|
6
|
+
constructor(computeFn, options) {
|
|
7
7
|
let initialValue = Computed._EMPTY;
|
|
8
8
|
const effect = new Effect(() => {
|
|
9
9
|
if (initialValue === Computed._EMPTY) {
|
|
10
10
|
initialValue = computeFn();
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
+
this._rang = effect._rang;
|
|
13
14
|
this.value = computeFn();
|
|
14
|
-
}
|
|
15
|
-
super(initialValue
|
|
15
|
+
});
|
|
16
|
+
super(initialValue, {
|
|
17
|
+
devtoolsName: 'Computed',
|
|
18
|
+
...options,
|
|
19
|
+
});
|
|
16
20
|
this._effect = effect;
|
|
17
21
|
}
|
|
18
22
|
unsubscribe() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { SubscriptionLike } from "rxjs";
|
|
2
2
|
export declare class Effect implements SubscriptionLike {
|
|
3
|
-
private _doLog;
|
|
4
|
-
closed: boolean;
|
|
5
3
|
private _subscriptions;
|
|
6
|
-
|
|
4
|
+
closed: boolean;
|
|
5
|
+
_rang: number;
|
|
6
|
+
constructor(effectFn: (ctx: (fn: () => void) => void) => void);
|
|
7
7
|
/**
|
|
8
8
|
* Выполняет функцию в tracked-контексте, подписываясь на Tracker.
|
|
9
9
|
*/
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Batcher } from "./Batcher";
|
|
2
|
+
import { Tracker } from "./Tracker";
|
|
3
|
+
export class Effect {
|
|
4
|
+
_subscriptions = [];
|
|
5
|
+
closed = false;
|
|
6
|
+
_rang = 0;
|
|
7
|
+
constructor(effectFn) {
|
|
8
|
+
this._runInTrackedContext(effectFn, false);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Выполняет функцию в tracked-контексте, подписываясь на Tracker.
|
|
12
|
+
*/
|
|
13
|
+
_runInTrackedContext(effectFn, isAsyncRun = false) {
|
|
14
|
+
// Отписываемся от предыдущих подписок
|
|
15
|
+
if (!isAsyncRun) {
|
|
16
|
+
this._rang = 0;
|
|
17
|
+
this._subscriptions.forEach((sub) => sub.unsubscribe());
|
|
18
|
+
this._subscriptions = [];
|
|
19
|
+
}
|
|
20
|
+
let isTrackedContext = true;
|
|
21
|
+
// TODO подумать как организовать планировщик при асинхронном запуске
|
|
22
|
+
let scheduler;
|
|
23
|
+
const scheduledFn = () => {
|
|
24
|
+
this._runInTrackedContext(effectFn);
|
|
25
|
+
};
|
|
26
|
+
// Подписываемся на Tracker. Во время выполнения подпишемся на все tracked наблюдатели.
|
|
27
|
+
const trackerSub = Tracker.tracked$.subscribe((tracked) => {
|
|
28
|
+
if (!isTrackedContext)
|
|
29
|
+
return;
|
|
30
|
+
if (tracked.rang <= this._rang) {
|
|
31
|
+
this._rang = tracked.rang + 1;
|
|
32
|
+
}
|
|
33
|
+
this._subscriptions.push(tracked.obsv$.subscribe(() => {
|
|
34
|
+
if (isTrackedContext) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
scheduler.schedule(scheduledFn);
|
|
38
|
+
}));
|
|
39
|
+
});
|
|
40
|
+
effectFn((fn) => {
|
|
41
|
+
this._runInTrackedContext(fn, true);
|
|
42
|
+
});
|
|
43
|
+
trackerSub.unsubscribe();
|
|
44
|
+
isTrackedContext = false;
|
|
45
|
+
scheduler = Batcher.scheduler(this._rang);
|
|
46
|
+
}
|
|
47
|
+
unsubscribe() {
|
|
48
|
+
this.closed = true;
|
|
49
|
+
this._subscriptions.forEach((sub) => sub.unsubscribe());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -2,6 +2,8 @@ import { Observable, Subscriber, TeardownLogic } from "rxjs";
|
|
|
2
2
|
import { SyncObservable } from "./SyncObservable";
|
|
3
3
|
import type { ReadableSignalLike } from "./types";
|
|
4
4
|
export declare class ReadonlySignal<T> extends SyncObservable<T> implements ReadableSignalLike<T> {
|
|
5
|
+
private readonly _devtools;
|
|
6
|
+
private static _logIdIndex;
|
|
5
7
|
constructor(subscribe?: (this: Observable<T>, subscriber: Subscriber<T>) => TeardownLogic);
|
|
6
8
|
get value(): T;
|
|
7
9
|
peek(): T;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SharedOptions } from "../../query/core/SharedOptions";
|
|
2
|
+
import { SyncObservable } from "./SyncObservable";
|
|
3
|
+
import { Tracker } from "./Tracker";
|
|
4
|
+
export class ReadonlySignal extends SyncObservable {
|
|
5
|
+
_devtools;
|
|
6
|
+
static _logIdIndex = 0;
|
|
7
|
+
constructor(subscribe) {
|
|
8
|
+
super(subscribe);
|
|
9
|
+
const stateDevtools = SharedOptions.DEVTOOLS?.state;
|
|
10
|
+
if (stateDevtools) {
|
|
11
|
+
const id = ReadonlySignal._logIdIndex++;
|
|
12
|
+
const key = `ReadonlySignal:i=${id}`;
|
|
13
|
+
const initialValue = this.peek();
|
|
14
|
+
this._devtools = stateDevtools(key, initialValue);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
get value() {
|
|
18
|
+
Tracker.next_legacy(this);
|
|
19
|
+
return super.value;
|
|
20
|
+
}
|
|
21
|
+
peek() {
|
|
22
|
+
return super.value;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated
|
|
26
|
+
*/
|
|
27
|
+
get() {
|
|
28
|
+
return this.value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { BehaviorSubject, Observable } from "rxjs";
|
|
2
2
|
import { type ReadableSignalLike, UnaryFunction } from "./types";
|
|
3
|
+
type SignalOptions = {
|
|
4
|
+
disableDevtools?: boolean;
|
|
5
|
+
devtoolsName?: string;
|
|
6
|
+
};
|
|
3
7
|
export declare class Signal<T> extends BehaviorSubject<T> implements ReadableSignalLike<T> {
|
|
4
|
-
|
|
8
|
+
private readonly _devtools;
|
|
9
|
+
protected _rang: number;
|
|
10
|
+
constructor(initialValue: T, options?: SignalOptions);
|
|
5
11
|
protected _onChange(value: T): void;
|
|
6
12
|
get value(): T;
|
|
7
13
|
set value(value: T);
|
|
@@ -11,6 +17,10 @@ export declare class Signal<T> extends BehaviorSubject<T> implements ReadableSig
|
|
|
11
17
|
* @deprecated use `value` instead.
|
|
12
18
|
*/
|
|
13
19
|
get(): T;
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated use `peek()` instead.
|
|
22
|
+
*/
|
|
23
|
+
getValue(): T;
|
|
14
24
|
/**
|
|
15
25
|
* @deprecated use `next(value)` instead.
|
|
16
26
|
*/
|
|
@@ -28,3 +38,4 @@ export declare class Signal<T> extends BehaviorSubject<T> implements ReadableSig
|
|
|
28
38
|
pipe<A extends Observable<any>, B extends Observable<any>, C extends Observable<any>, D extends Observable<any>, E extends Observable<any>, F extends Observable<any>, G extends Observable<any>, H extends Observable<any>, I extends Observable<any>>(op1: UnaryFunction<Signal<T>, A>, op2: UnaryFunction<A, B>, op3: UnaryFunction<B, C>, op4: UnaryFunction<C, D>, op5: UnaryFunction<D, E>, op6: UnaryFunction<E, F>, op7: UnaryFunction<F, G>, op8: UnaryFunction<G, H>, op9: UnaryFunction<H, I>, ...operations: UnaryFunction<Observable<any>, Observable<any>>[]): Observable<unknown>;
|
|
29
39
|
asReadonly(): ReadableSignalLike<T>;
|
|
30
40
|
}
|
|
41
|
+
export {};
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import { SharedOptions } from "../../query/core/SharedOptions";
|
|
1
2
|
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
import { Batcher } from "./Batcher";
|
|
4
|
+
import { Indexer } from "./Indexer";
|
|
2
5
|
import { Tracker } from "./Tracker";
|
|
3
6
|
export class Signal extends BehaviorSubject {
|
|
4
|
-
|
|
7
|
+
_devtools;
|
|
8
|
+
_rang = 0;
|
|
9
|
+
constructor(initialValue, options) {
|
|
5
10
|
super(initialValue);
|
|
11
|
+
const stateDevtools = SharedOptions.DEVTOOLS?.state;
|
|
12
|
+
if (stateDevtools && options?.disableDevtools !== true) {
|
|
13
|
+
const id = Indexer.getIndex();
|
|
14
|
+
const key = `${options?.devtoolsName || 'Signal'}:i=${id}`;
|
|
15
|
+
this._devtools = stateDevtools(key, initialValue);
|
|
16
|
+
}
|
|
6
17
|
}
|
|
7
18
|
_onChange(value) {
|
|
8
|
-
|
|
19
|
+
Batcher.batch(() => {
|
|
20
|
+
this._devtools?.(value);
|
|
21
|
+
super.next(value);
|
|
22
|
+
});
|
|
9
23
|
}
|
|
10
24
|
get value() {
|
|
11
|
-
Tracker.next(this);
|
|
25
|
+
Tracker.next(this._rang, this);
|
|
12
26
|
return super.value;
|
|
13
27
|
}
|
|
14
28
|
set value(value) {
|
|
@@ -26,6 +40,12 @@ export class Signal extends BehaviorSubject {
|
|
|
26
40
|
get() {
|
|
27
41
|
return this.value;
|
|
28
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* @deprecated use `peek()` instead.
|
|
45
|
+
*/
|
|
46
|
+
getValue() {
|
|
47
|
+
return super.getValue();
|
|
48
|
+
}
|
|
29
49
|
/**
|
|
30
50
|
* @deprecated use `next(value)` instead.
|
|
31
51
|
*/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Observable, Subject } from "rxjs";
|
|
2
|
+
type TrackedValue = {
|
|
3
|
+
rang: number;
|
|
4
|
+
obsv$: Observable<unknown>;
|
|
5
|
+
};
|
|
6
|
+
export declare const Tracker: {
|
|
7
|
+
/** @deprecated */
|
|
8
|
+
tracked_legacy$: Subject<Observable<unknown>>;
|
|
9
|
+
/** @deprecated */
|
|
10
|
+
next_legacy(value: Observable<unknown>): void;
|
|
11
|
+
tracked$: Subject<TrackedValue>;
|
|
12
|
+
next(rang: number, observable: Observable<unknown>): void;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Subject } from "rxjs";
|
|
2
|
+
export const Tracker = {
|
|
3
|
+
/** @deprecated */
|
|
4
|
+
tracked_legacy$: new Subject(),
|
|
5
|
+
/** @deprecated */
|
|
6
|
+
next_legacy(value) {
|
|
7
|
+
Tracker.tracked_legacy$.next(value);
|
|
8
|
+
},
|
|
9
|
+
tracked$: new Subject(),
|
|
10
|
+
next(rang, observable) {
|
|
11
|
+
Tracker.tracked$.next({ rang, obsv$: observable, });
|
|
12
|
+
}
|
|
13
|
+
};
|
|
@@ -12,4 +12,3 @@ export interface UnaryFunction<T, R> {
|
|
|
12
12
|
}
|
|
13
13
|
export type SignalOperatorFn<T, R> = UnaryFunction<ReadableSignalLike<T>, ReadableSignalLike<R>>;
|
|
14
14
|
export type MonoTypeSignalOperatorFn<T> = SignalOperatorFn<T, T>;
|
|
15
|
-
export type ObservableToSignalFn<T> = UnaryFunction<Observable<T>, ReadableSignalLike<T>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { signalize } from "../../signal/operators";
|
|
2
1
|
import { z } from "zod/v4";
|
|
3
|
-
import { Computed } from "
|
|
2
|
+
import { Computed } from "../base";
|
|
3
|
+
import { signalize } from "../operators";
|
|
4
4
|
const NullOrString = z.string().nullable();
|
|
5
5
|
const NONE = Symbol('NONE');
|
|
6
6
|
export class LocalSignal extends Computed {
|
|
@@ -56,7 +56,7 @@ export class LocalSignal extends Computed {
|
|
|
56
56
|
return checkEffect(value) ? value : defaultValue;
|
|
57
57
|
}
|
|
58
58
|
return value;
|
|
59
|
-
});
|
|
59
|
+
}, { devtoolsName: 'LocalSignal' });
|
|
60
60
|
this._options = _options;
|
|
61
61
|
}
|
|
62
62
|
_onChange(value) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createOperatorSubscriber } from "rxjs/internal/operators/OperatorSubscriber";
|
|
2
|
-
import { ReadonlySignal } from "
|
|
2
|
+
import { ReadonlySignal } from "../base";
|
|
3
3
|
export function filterUpdates(predicate, thisArg) {
|
|
4
4
|
return (source) => new ReadonlySignal((destination) => {
|
|
5
5
|
let index = 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createOperatorSubscriber } from "rxjs/internal/operators/OperatorSubscriber";
|
|
2
|
-
import { ReadonlySignal } from "
|
|
2
|
+
import { ReadonlySignal } from "../base";
|
|
3
3
|
export function mapSignals(project, thisArg) {
|
|
4
4
|
return (source) => new ReadonlySignal((destination) => {
|
|
5
5
|
let index = 0;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Devtools
|
|
2
|
+
|
|
3
|
+
RxToolkit предоставляет интеграцию с популярными инструментами разработчика для отладки реактивных приложений в реальном времени. Вы можете отслеживать изменения сигналов, выполнение операций и состояние ресурсов, анализировать граф зависимостей и исследовать поведение реактивной системы.
|
|
4
|
+
|
|
5
|
+
**Отслеживает изменения состояния:**
|
|
6
|
+
- Сигналов (Signal/Computed)
|
|
7
|
+
- Ресурсов и операций (Resource/Operation)
|
|
8
|
+
|
|
9
|
+
## Redux DevTools
|
|
10
|
+
|
|
11
|
+
Популярное браузерное расширение для отладки состояния приложений. **RxToolkit включает встроенный адаптер `reduxDevtools()`**.
|
|
12
|
+
|
|
13
|
+
**Установка:**
|
|
14
|
+
1. Установите [расширение](https://github.com/reduxjs/redux-devtools) для браузера
|
|
15
|
+
2. Подключите в коде:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { DefaultOptions, reduxDevtools } from '@fozy-labs/rx-toolkit';
|
|
19
|
+
|
|
20
|
+
DefaultOptions.update({
|
|
21
|
+
DEVTOOLS: reduxDevtools()
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## @reatom/devtools
|
|
26
|
+
|
|
27
|
+
Npm пакет, содержащий отладчик, работающий прямо в браузере.
|
|
28
|
+
Совместим по API с `rx-toolkit`.
|
|
29
|
+
После подключения в углу страницы появляется кнопка,
|
|
30
|
+
которая открывает панель инструментов.
|
|
31
|
+
|
|
32
|
+
**Установка:**
|
|
33
|
+
```bash
|
|
34
|
+
npm install @reatom/devtools
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Подключение:**
|
|
38
|
+
```typescript
|
|
39
|
+
import { DefaultOptions } from '@fozy-labs/rx-toolkit';
|
|
40
|
+
import { createDevtools } from '@reatom/devtools';
|
|
41
|
+
|
|
42
|
+
DefaultOptions.update({
|
|
43
|
+
DEVTOOLS: createDevtools({
|
|
44
|
+
initVisibility: true
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Может пригодиться:**
|
|
50
|
+
- Если в вашей среде не удобно или проблематично установить браузерное расширение.
|
|
51
|
+
- Если вы уже работали с Reatom.
|
|
52
|
+
|
|
53
|
+
## Практики
|
|
54
|
+
|
|
55
|
+
### Development-режим
|
|
56
|
+
|
|
57
|
+
Подключайте devtools только в разработке:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// Node.js/Webpack
|
|
61
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
62
|
+
DefaultOptions.update({ DEVTOOLS: reduxDevtools() });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Vite
|
|
66
|
+
if (import.meta.env.DEV) {
|
|
67
|
+
DefaultOptions.update({ DEVTOOLS: reduxDevtools() });
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### SSR-совместимость
|
|
72
|
+
|
|
73
|
+
Защитите от выполнения на сервере:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'production') {
|
|
77
|
+
DefaultOptions.update({ DEVTOOLS: reduxDevtools() });
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Несколько инструментов
|
|
82
|
+
|
|
83
|
+
Можно комбинировать несколько devtools:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { combineDevtools, reduxDevtools } from '@fozy-labs/rx-toolkit';
|
|
87
|
+
import { createDevtools } from '@reatom/devtools';
|
|
88
|
+
|
|
89
|
+
DefaultOptions.update({
|
|
90
|
+
DEVTOOLS: combineDevtools(
|
|
91
|
+
reduxDevtools({ name: 'MyApp' }),
|
|
92
|
+
createDevtools({ initVisibility: true })
|
|
93
|
+
)
|
|
94
|
+
});
|
|
95
|
+
```
|