@fozy-labs/rx-toolkit 0.4.18 → 0.5.0-rc.1
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 +20 -19
- package/dist/common/devtools/reduxDevtools.d.ts +17 -0
- package/dist/common/devtools/reduxDevtools.js +98 -17
- package/dist/common/devtools/types.d.ts +1 -0
- package/dist/common/options/DefaultOptions.d.ts +0 -2
- package/dist/common/options/DefaultOptions.js +0 -2
- package/dist/common/options/SharedOptions.d.ts +0 -2
- package/dist/common/options/SharedOptions.js +0 -1
- package/dist/query/api/resetAllQueriesCache.d.ts +1 -0
- package/dist/query/api/resetAllQueriesCache.js +4 -0
- package/dist/query/core/Opertation/Operation.js +10 -7
- package/dist/query/core/Opertation/OperationAgent.d.ts +0 -2
- package/dist/query/core/Opertation/OperationAgent.js +4 -21
- package/dist/query/core/QueriesCache.d.ts +1 -1
- package/dist/query/core/QueriesCache.js +2 -6
- package/dist/query/core/{CleanAllQueriesSignal.d.ts → ResetAllQueriesSignal.d.ts} +1 -1
- package/dist/query/core/ResetAllQueriesSignal.js +11 -0
- package/dist/query/core/Resource/Resource.js +7 -3
- package/dist/query/core/Resource/ResourceAgent.d.ts +2 -3
- package/dist/query/core/Resource/ResourceAgent.js +62 -29
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/lib/IndirectMap.d.ts +1 -1
- package/dist/query/lib/IndirectMap.js +1 -1
- package/dist/query/lib/ReactiveCache.d.ts +1 -1
- package/dist/query/lib/ReactiveCache.js +2 -2
- package/dist/query/react/useOperationAgent.js +2 -5
- package/dist/query/react/useResourceAgent.js +1 -4
- package/dist/query/types/Operation.types.d.ts +1 -3
- package/dist/query/types/Resource.types.d.ts +1 -3
- package/dist/signals/base/Batcher.d.ts +1 -1
- package/dist/signals/base/Batcher.js +1 -1
- package/dist/signals/base/DependencyTracker.d.ts +18 -0
- package/dist/signals/base/DependencyTracker.js +13 -0
- package/dist/signals/base/Devtools.d.ts +1 -1
- package/dist/signals/base/Devtools.js +13 -1
- package/dist/signals/base/ReadonlySignal.d.ts +5 -7
- package/dist/signals/base/ReadonlySignal.js +20 -12
- package/dist/signals/base/index.d.ts +1 -2
- package/dist/signals/base/index.js +1 -2
- package/dist/signals/operators/index.d.ts +0 -2
- package/dist/signals/operators/index.js +0 -2
- package/dist/signals/operators/signalize.d.ts +2 -2
- package/dist/signals/operators/signalize.js +1 -1
- package/dist/signals/react/useSignal.d.ts +6 -2
- package/dist/signals/react/useSignal.js +2 -21
- package/dist/signals/signals/Computed.d.ts +13 -11
- package/dist/signals/signals/Computed.js +79 -26
- package/dist/signals/signals/Effect.d.ts +11 -7
- package/dist/signals/signals/Effect.js +60 -58
- package/dist/signals/signals/LocalSignal.d.ts +14 -7
- package/dist/signals/signals/LocalSignal.js +53 -36
- package/dist/signals/signals/Signal.d.ts +13 -37
- package/dist/signals/signals/Signal.js +44 -58
- package/dist/signals/types/index.d.ts +1 -0
- package/dist/signals/types/index.js +1 -0
- package/dist/signals/types/signals.types.d.ts +16 -0
- package/docs/CHANGELOG.md +32 -0
- package/docs/devtools/README.md +162 -29
- package/docs/migrations/0.5.0.md +73 -0
- package/docs/options/README.md +89 -0
- package/docs/query/README.md +425 -89
- package/docs/release/README.md +58 -6
- package/docs/signals/README.md +207 -34
- package/docs/usage/react/README.md +261 -49
- package/package.json +1 -1
- package/dist/query/api/cleanAllQueriesCache.d.ts +0 -1
- package/dist/query/api/cleanAllQueriesCache.js +0 -4
- package/dist/query/core/CleanAllQueriesSignal.js +0 -11
- package/dist/signals/base/Tracker.d.ts +0 -10
- package/dist/signals/base/Tracker.js +0 -7
- package/dist/signals/base/types.d.ts +0 -23
- package/dist/signals/operators/filterUpdates.d.ts +0 -5
- package/dist/signals/operators/filterUpdates.js +0 -18
- package/dist/signals/operators/mapSignals.d.ts +0 -3
- package/dist/signals/operators/mapSignals.js +0 -10
- /package/dist/signals/{base/types.js → types/signals.types.js} +0 -0
|
@@ -1,87 +1,89 @@
|
|
|
1
|
-
import { Batcher,
|
|
2
|
-
import { SharedOptions } from "../../common/options/SharedOptions";
|
|
1
|
+
import { Batcher, DependencyTracker } from "../base";
|
|
3
2
|
export class Effect {
|
|
4
|
-
|
|
5
|
-
_subscriptions = [];
|
|
3
|
+
_subscriptions = new Map();
|
|
6
4
|
_teardown;
|
|
7
|
-
_scopeDestroyedSub = SharedOptions.getScopeDestroyed$?.()?.subscribe(() => {
|
|
8
|
-
this.complete();
|
|
9
|
-
});
|
|
10
5
|
closed = false;
|
|
11
6
|
_rang = 0;
|
|
12
|
-
constructor(effectFn
|
|
13
|
-
this.
|
|
14
|
-
this._runInTrackedContext(effectFn, false);
|
|
7
|
+
constructor(effectFn) {
|
|
8
|
+
this._runInTrackedContext(effectFn);
|
|
15
9
|
}
|
|
16
10
|
/**
|
|
17
11
|
* Выполняет функцию в tracked-контексте, подписываясь на Tracker.
|
|
18
12
|
*/
|
|
19
|
-
_runInTrackedContext(effectFn
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this._teardown = undefined;
|
|
25
|
-
this._rang = 0;
|
|
26
|
-
prevSubscriptions = this._subscriptions;
|
|
27
|
-
this._subscriptions = [];
|
|
28
|
-
}
|
|
29
|
-
let isTrackedContext = true;
|
|
30
|
-
// TODO подумать как организовать планировщик при асинхронном запуске
|
|
13
|
+
_runInTrackedContext(effectFn) {
|
|
14
|
+
this._callTeardown();
|
|
15
|
+
this._rang = 0;
|
|
16
|
+
const legacySubscriptions = this._subscriptions;
|
|
17
|
+
this._subscriptions = new Map();
|
|
31
18
|
let scheduler;
|
|
19
|
+
// Стабильная функция для планирования выполнения эффекта
|
|
32
20
|
const scheduledFn = () => {
|
|
33
21
|
this._runInTrackedContext(effectFn);
|
|
34
22
|
};
|
|
35
|
-
//
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
23
|
+
// Функция для проверки и создания подписки на зависимость
|
|
24
|
+
const checkSubscription = (obs) => {
|
|
25
|
+
if (this._subscriptions.has(obs)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const legacySub = legacySubscriptions.get(obs);
|
|
29
|
+
if (legacySub) {
|
|
30
|
+
legacySubscriptions.delete(obs);
|
|
31
|
+
this._subscriptions.set(obs, legacySub);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const sub = obs.subscribe(() => {
|
|
35
|
+
if (isTrackedContext) {
|
|
39
36
|
return;
|
|
40
|
-
if (tracked.rang <= this._rang) {
|
|
41
|
-
this._rang = tracked.rang + 1;
|
|
42
37
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.complete();
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
const teardown = effectFn((fn) => {
|
|
59
|
-
this._runInTrackedContext(fn, true);
|
|
38
|
+
scheduler.schedule(scheduledFn);
|
|
39
|
+
});
|
|
40
|
+
this._subscriptions.set(obs, sub);
|
|
41
|
+
return sub;
|
|
42
|
+
};
|
|
43
|
+
let isTrackedContext = true;
|
|
44
|
+
const stopTracking = DependencyTracker.start((dependency) => {
|
|
45
|
+
checkSubscription(dependency.obs);
|
|
46
|
+
const dependencyRang = dependency.getRang();
|
|
47
|
+
if (dependencyRang >= this._rang) {
|
|
48
|
+
this._rang = dependencyRang + 1;
|
|
49
|
+
}
|
|
60
50
|
});
|
|
51
|
+
const optionalTeardown = effectFn();
|
|
52
|
+
stopTracking();
|
|
53
|
+
isTrackedContext = false;
|
|
61
54
|
// Сохраняем teardown функцию, если она была возвращена
|
|
62
|
-
if (typeof
|
|
63
|
-
this._teardown =
|
|
55
|
+
if (typeof optionalTeardown === 'function') {
|
|
56
|
+
this._teardown = optionalTeardown;
|
|
64
57
|
}
|
|
65
|
-
trackerSub.unsubscribe();
|
|
66
|
-
isTrackedContext = false;
|
|
67
58
|
scheduler = Batcher.scheduler(this._rang);
|
|
68
|
-
|
|
59
|
+
legacySubscriptions?.forEach((sub) => {
|
|
60
|
+
sub.unsubscribe();
|
|
61
|
+
});
|
|
69
62
|
}
|
|
70
|
-
|
|
63
|
+
unsubscribe() {
|
|
71
64
|
if (this.closed)
|
|
72
65
|
return;
|
|
73
66
|
this.closed = true;
|
|
74
67
|
// Вызываем teardown перед завершением эффекта
|
|
75
|
-
this.
|
|
76
|
-
this._teardown = undefined;
|
|
68
|
+
this._callTeardown();
|
|
77
69
|
this._subscriptions.forEach((sub) => sub.unsubscribe());
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
}
|
|
71
|
+
_getRang() {
|
|
72
|
+
return this._rang;
|
|
80
73
|
}
|
|
81
74
|
/**
|
|
82
|
-
* @deprecated Use `
|
|
75
|
+
* @deprecated Use `unsubscribe()` method instead.
|
|
83
76
|
*/
|
|
84
|
-
|
|
85
|
-
this.
|
|
77
|
+
complete() {
|
|
78
|
+
this.unsubscribe();
|
|
79
|
+
}
|
|
80
|
+
_callTeardown() {
|
|
81
|
+
if (this._teardown) {
|
|
82
|
+
this._teardown();
|
|
83
|
+
this._teardown = undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
static create(effectFn) {
|
|
87
|
+
return new Effect(effectFn);
|
|
86
88
|
}
|
|
87
89
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ZodType } from "zod/v4";
|
|
2
2
|
import { Observable } from "rxjs";
|
|
3
|
-
import {
|
|
3
|
+
import { SignalFn } from "../../signals/types";
|
|
4
|
+
import { StateDevtoolsOptions } from "../../common/devtools";
|
|
4
5
|
type Options<T> = {
|
|
5
6
|
zodSchema?: ZodType<T>;
|
|
6
7
|
key: string;
|
|
@@ -11,15 +12,21 @@ type Options<T> = {
|
|
|
11
12
|
validator$?: Observable<(value: T) => boolean>;
|
|
12
13
|
checkEffect?: (value: T) => boolean;
|
|
13
14
|
defaultValue: T;
|
|
14
|
-
|
|
15
|
+
devtoolsOptions?: StateDevtoolsOptions;
|
|
15
16
|
};
|
|
16
|
-
export declare class LocalSignal<T = string | null | number | undefined>
|
|
17
|
-
private
|
|
17
|
+
export declare class LocalSignal<T = string | null | number | undefined> {
|
|
18
|
+
private _signal;
|
|
19
|
+
private _computed;
|
|
20
|
+
private readonly _options;
|
|
21
|
+
readonly obs: Observable<T>;
|
|
22
|
+
constructor(options: Options<T>);
|
|
23
|
+
set(value: T): void;
|
|
24
|
+
peek(): T;
|
|
25
|
+
get(): T;
|
|
18
26
|
static KEY_PREFIX: string;
|
|
19
27
|
static DRIVER: Storage;
|
|
20
|
-
|
|
28
|
+
static create<T = string | null | number | undefined>(options: Options<T>): SignalFn<T>;
|
|
29
|
+
static _getStorageValue(options: Options<any>): any;
|
|
21
30
|
private static _setStorageValue;
|
|
22
|
-
constructor(_options: Options<T>);
|
|
23
|
-
protected _onChange(value: T): void;
|
|
24
31
|
}
|
|
25
32
|
export {};
|
|
@@ -1,18 +1,66 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
|
-
import { Computed } from "../signals";
|
|
3
2
|
import { signalize } from "../operators";
|
|
4
|
-
|
|
3
|
+
import { Signal } from "./Signal";
|
|
4
|
+
import { Computed } from "./Computed";
|
|
5
5
|
const NONE = Symbol('NONE');
|
|
6
|
-
export class LocalSignal
|
|
6
|
+
export class LocalSignal {
|
|
7
|
+
_signal;
|
|
8
|
+
_computed;
|
|
7
9
|
_options;
|
|
10
|
+
obs;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
let initialValue = LocalSignal._getStorageValue(options);
|
|
13
|
+
if (initialValue === NONE) {
|
|
14
|
+
initialValue = options.defaultValue;
|
|
15
|
+
}
|
|
16
|
+
this._signal = new Signal(initialValue, { isDisabled: true });
|
|
17
|
+
const validatorSignal$ = options.validator$ && signalize(options.validator$);
|
|
18
|
+
this._computed = new Computed(() => {
|
|
19
|
+
const value = this._signal.get();
|
|
20
|
+
if (validatorSignal$) {
|
|
21
|
+
const validator = validatorSignal$.get();
|
|
22
|
+
if (!validator(value)) {
|
|
23
|
+
return options.defaultValue;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (options.checkEffect) {
|
|
27
|
+
return options.checkEffect(value) ? value : options.defaultValue;
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}, options.devtoolsOptions);
|
|
31
|
+
this.obs = this._computed.obs;
|
|
32
|
+
this._options = options;
|
|
33
|
+
}
|
|
34
|
+
set(value) {
|
|
35
|
+
LocalSignal._setStorageValue(this._options, value);
|
|
36
|
+
this._signal.set(value);
|
|
37
|
+
}
|
|
38
|
+
peek() {
|
|
39
|
+
return this._computed.peek();
|
|
40
|
+
}
|
|
41
|
+
get() {
|
|
42
|
+
return this._computed.get();
|
|
43
|
+
}
|
|
44
|
+
// === static ===
|
|
8
45
|
static KEY_PREFIX = '__LSValue__';
|
|
9
46
|
static DRIVER = localStorage;
|
|
47
|
+
static create(options) {
|
|
48
|
+
const localSignal = new LocalSignal(options);
|
|
49
|
+
function signalFn() {
|
|
50
|
+
return localSignal.get();
|
|
51
|
+
}
|
|
52
|
+
signalFn.peek = () => localSignal.peek();
|
|
53
|
+
signalFn.get = () => localSignal.get();
|
|
54
|
+
signalFn.set = (value) => localSignal.set(value);
|
|
55
|
+
signalFn.obs = localSignal.obs;
|
|
56
|
+
return signalFn;
|
|
57
|
+
}
|
|
10
58
|
static _getStorageValue(options) {
|
|
11
59
|
const storageKey = `${LocalSignal.KEY_PREFIX}:${options.key}`;
|
|
12
60
|
const item = LocalSignal.DRIVER.getItem(storageKey);
|
|
13
61
|
if (!item)
|
|
14
62
|
return NONE;
|
|
15
|
-
const schema = z.record(z.string(), options.zodSchema ||
|
|
63
|
+
const schema = z.record(z.string(), options.zodSchema || z.any());
|
|
16
64
|
const parsed = schema.safeParse(JSON.parse(item));
|
|
17
65
|
if (!parsed.success) {
|
|
18
66
|
console.warn(`Invalid value for key "${options.key}" in localStorage`, parsed.error);
|
|
@@ -27,7 +75,7 @@ export class LocalSignal extends Computed {
|
|
|
27
75
|
static _setStorageValue(options, value) {
|
|
28
76
|
const storageKey = `${LocalSignal.KEY_PREFIX}:${options.key}`;
|
|
29
77
|
const item = LocalSignal.DRIVER.getItem(storageKey) || '{}';
|
|
30
|
-
const schema = z.record(z.string(), options.zodSchema ||
|
|
78
|
+
const schema = z.record(z.string(), options.zodSchema || z.any());
|
|
31
79
|
const parsed = schema.safeParse(JSON.parse(item));
|
|
32
80
|
let data = parsed.data ?? {};
|
|
33
81
|
if (!parsed.success) {
|
|
@@ -37,35 +85,4 @@ export class LocalSignal extends Computed {
|
|
|
37
85
|
data[subKey] = value;
|
|
38
86
|
LocalSignal.DRIVER.setItem(storageKey, JSON.stringify(data));
|
|
39
87
|
}
|
|
40
|
-
constructor(_options) {
|
|
41
|
-
const validator$ = _options.validator$;
|
|
42
|
-
const checkEffect = _options.checkEffect;
|
|
43
|
-
const defaultValue = _options.defaultValue;
|
|
44
|
-
const devtoolsName = _options.devtoolsName;
|
|
45
|
-
const validatorSignal$ = validator$ && signalize(validator$);
|
|
46
|
-
super(() => {
|
|
47
|
-
const value = LocalSignal._getStorageValue(_options);
|
|
48
|
-
if (value === NONE)
|
|
49
|
-
return defaultValue;
|
|
50
|
-
if (validatorSignal$) {
|
|
51
|
-
const validator = validatorSignal$.value;
|
|
52
|
-
if (!validator(value)) {
|
|
53
|
-
return defaultValue;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (checkEffect) {
|
|
57
|
-
return checkEffect(value) ? value : defaultValue;
|
|
58
|
-
}
|
|
59
|
-
return value;
|
|
60
|
-
}, {
|
|
61
|
-
isDisabled: devtoolsName === false,
|
|
62
|
-
base: 'LocalSignals',
|
|
63
|
-
name: devtoolsName || undefined,
|
|
64
|
-
});
|
|
65
|
-
this._options = _options;
|
|
66
|
-
}
|
|
67
|
-
_onChange(value) {
|
|
68
|
-
LocalSignal._setStorageValue(this._options, value);
|
|
69
|
-
return super._onChange(value);
|
|
70
|
-
}
|
|
71
88
|
}
|
|
@@ -1,43 +1,19 @@
|
|
|
1
|
-
import { BehaviorSubject
|
|
2
|
-
import { ReadableSignalLike, SignalLike, UnaryFunction } from "../base";
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
3
2
|
import { StateDevtoolsOptions } from "../../common/devtools";
|
|
4
|
-
|
|
3
|
+
import { SignalFn } from "../../signals/types";
|
|
4
|
+
import { Effect } from "../../signals";
|
|
5
|
+
export declare class Signal<T> {
|
|
5
6
|
private readonly _stateDevtools;
|
|
6
|
-
|
|
7
|
-
protected readonly
|
|
7
|
+
private _rang;
|
|
8
|
+
protected readonly bs$: BehaviorSubject<T>;
|
|
9
|
+
readonly obs: import("rxjs").Observable<T>;
|
|
8
10
|
constructor(initialValue: T, options?: StateDevtoolsOptions);
|
|
9
|
-
protected _onChange(value: T): void;
|
|
10
|
-
get value(): T;
|
|
11
|
-
set value(value: T);
|
|
12
|
-
next(value: T): void;
|
|
13
11
|
peek(): T;
|
|
14
|
-
/**
|
|
15
|
-
* @deprecated use `value` instead.
|
|
16
|
-
*/
|
|
17
|
-
get(): T;
|
|
18
|
-
/**
|
|
19
|
-
* @deprecated use `peek()` instead.
|
|
20
|
-
*/
|
|
21
|
-
getValue(): T;
|
|
22
|
-
/**
|
|
23
|
-
* @deprecated use `next(value)` instead.
|
|
24
|
-
*/
|
|
25
12
|
set(value: T): void;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
pipe(): Signal<T>;
|
|
33
|
-
pipe<A extends Observable<any>>(op1: UnaryFunction<ReadableSignalLike<T>, A>): A;
|
|
34
|
-
pipe<A extends Observable<any>, B extends Observable<any>>(op1: UnaryFunction<Signal<T>, A>, op2: UnaryFunction<A, B>): B;
|
|
35
|
-
pipe<A extends Observable<any>, B extends Observable<any>, C extends Observable<any>>(op1: UnaryFunction<Signal<T>, A>, op2: UnaryFunction<A, B>, op3: UnaryFunction<B, C>): C;
|
|
36
|
-
pipe<A extends Observable<any>, B extends Observable<any>, C extends Observable<any>, D extends Observable<any>>(op1: UnaryFunction<Signal<T>, A>, op2: UnaryFunction<A, B>, op3: UnaryFunction<B, C>, op4: UnaryFunction<C, D>): D;
|
|
37
|
-
pipe<A extends Observable<any>, B extends Observable<any>, C extends Observable<any>, D extends Observable<any>, E extends Observable<any>>(op1: UnaryFunction<Signal<T>, A>, op2: UnaryFunction<A, B>, op3: UnaryFunction<B, C>, op4: UnaryFunction<C, D>, op5: UnaryFunction<D, E>): E;
|
|
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>>(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>): F;
|
|
39
|
-
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>>(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>): G;
|
|
40
|
-
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>>(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>): H;
|
|
41
|
-
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>): I;
|
|
42
|
-
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>;
|
|
13
|
+
get(): T;
|
|
14
|
+
protected _onChange(value: T): void;
|
|
15
|
+
private static _finalizationRegistry;
|
|
16
|
+
static create<T>(initialValue: T, options?: StateDevtoolsOptions): SignalFn<T>;
|
|
17
|
+
static compute<T>(computeFn: () => T, options?: StateDevtoolsOptions): import("../../signals/types").ComputeFn<T>;
|
|
18
|
+
static effect(effectFn: () => void): Effect;
|
|
43
19
|
}
|
|
@@ -1,74 +1,60 @@
|
|
|
1
1
|
import { BehaviorSubject } from "rxjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
export class Signal
|
|
2
|
+
import { Computed, Effect } from "../../signals";
|
|
3
|
+
import { Batcher, DependencyTracker, Devtools } from "../base";
|
|
4
|
+
export class Signal {
|
|
5
5
|
_stateDevtools;
|
|
6
6
|
_rang = 0;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
7
|
+
bs$;
|
|
8
|
+
obs;
|
|
10
9
|
constructor(initialValue, options) {
|
|
11
|
-
|
|
10
|
+
this.bs$ = new BehaviorSubject(initialValue);
|
|
11
|
+
this.obs = this.bs$.asObservable();
|
|
12
12
|
this._stateDevtools = Devtools.createState(initialValue, {
|
|
13
|
-
base:
|
|
13
|
+
base: Signal.name,
|
|
14
14
|
...(typeof options === 'string' ? { name: options } : options)
|
|
15
15
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this._stateDevtools?.(value);
|
|
20
|
-
super.next(value);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
get value() {
|
|
24
|
-
Tracker.next(this._rang, this);
|
|
25
|
-
return super.value;
|
|
26
|
-
}
|
|
27
|
-
set value(value) {
|
|
28
|
-
this._onChange(value);
|
|
29
|
-
}
|
|
30
|
-
next(value) {
|
|
31
|
-
this._onChange(value);
|
|
16
|
+
if (this._stateDevtools) {
|
|
17
|
+
Signal._finalizationRegistry.register(this, this._stateDevtools);
|
|
18
|
+
}
|
|
32
19
|
}
|
|
33
20
|
peek() {
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* @deprecated use `value` instead.
|
|
38
|
-
*/
|
|
39
|
-
get() {
|
|
40
|
-
return this.value;
|
|
21
|
+
return this.bs$.getValue();
|
|
41
22
|
}
|
|
42
|
-
/**
|
|
43
|
-
* @deprecated use `peek()` instead.
|
|
44
|
-
*/
|
|
45
|
-
getValue() {
|
|
46
|
-
return super.getValue();
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* @deprecated use `next(value)` instead.
|
|
50
|
-
*/
|
|
51
23
|
set(value) {
|
|
52
|
-
return this.
|
|
53
|
-
}
|
|
54
|
-
complete() {
|
|
55
|
-
this._stateDevtools?.('$COMPLETED');
|
|
56
|
-
this._scopeDestroyedSub?.unsubscribe();
|
|
57
|
-
super.complete();
|
|
24
|
+
return this._onChange(value);
|
|
58
25
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
26
|
+
get() {
|
|
27
|
+
DependencyTracker.track({
|
|
28
|
+
getRang: () => this._rang,
|
|
29
|
+
obs: this.obs,
|
|
30
|
+
});
|
|
31
|
+
return this.bs$.getValue();
|
|
64
32
|
}
|
|
65
|
-
|
|
66
|
-
|
|
33
|
+
_onChange(value) {
|
|
34
|
+
Batcher.run(() => {
|
|
35
|
+
this._stateDevtools?.(value);
|
|
36
|
+
this.bs$.next(value);
|
|
37
|
+
});
|
|
67
38
|
}
|
|
68
|
-
|
|
69
|
-
|
|
39
|
+
// === static ===
|
|
40
|
+
static _finalizationRegistry = new FinalizationRegistry((heldValue) => {
|
|
41
|
+
heldValue('$COMPLETED');
|
|
42
|
+
});
|
|
43
|
+
static create(initialValue, options) {
|
|
44
|
+
const ls = new Signal(initialValue, options);
|
|
45
|
+
function signalFn() {
|
|
46
|
+
return ls.get();
|
|
47
|
+
}
|
|
48
|
+
signalFn.peek = () => ls.peek();
|
|
49
|
+
signalFn.set = (value) => ls.set(value);
|
|
50
|
+
signalFn.get = () => ls.get();
|
|
51
|
+
signalFn.obs = ls.obs;
|
|
52
|
+
return signalFn;
|
|
53
|
+
}
|
|
54
|
+
static compute(computeFn, options) {
|
|
55
|
+
return Computed.create(computeFn, options);
|
|
56
|
+
}
|
|
57
|
+
static effect(effectFn) {
|
|
58
|
+
return Effect.create(effectFn);
|
|
70
59
|
}
|
|
71
60
|
}
|
|
72
|
-
function pipeReducer(prev, fn) {
|
|
73
|
-
return fn(prev);
|
|
74
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './signals.types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './signals.types';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
export interface ReadableSignalLike<T> {
|
|
3
|
+
readonly obs: Observable<T>;
|
|
4
|
+
peek(): T;
|
|
5
|
+
get(): T;
|
|
6
|
+
}
|
|
7
|
+
export interface ReadableSignalFnLike<T> extends ReadableSignalLike<T> {
|
|
8
|
+
(): T;
|
|
9
|
+
}
|
|
10
|
+
export interface WriteableSignalLike<T> {
|
|
11
|
+
set(value: T): void;
|
|
12
|
+
}
|
|
13
|
+
export interface SignalFn<T> extends ReadableSignalFnLike<T>, WriteableSignalLike<T> {
|
|
14
|
+
}
|
|
15
|
+
export interface ComputeFn<T> extends ReadableSignalFnLike<T> {
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## Что нового в 0.5.0
|
|
4
|
+
|
|
5
|
+
[Гайд по миграции](./migrations/0.5.0.md)
|
|
6
|
+
|
|
7
|
+
### RxSignals
|
|
8
|
+
|
|
9
|
+
- **Функциональный API (рекомендуется)**: `Signal.create()`, `Signal.compute()`, `Signal.effect()`
|
|
10
|
+
- **Cleanup функции в Effect**: поддержка возврата teardown функции
|
|
11
|
+
- **Без complete()**: теперь нет необходимости вызывать `complete()` для Signal и Computed
|
|
12
|
+
- **Ленивый Computed**: вычисление только при наличии подписок
|
|
13
|
+
|
|
14
|
+
### RxQuery
|
|
15
|
+
|
|
16
|
+
- **Расширенные состояния**: `isInitialLoading`, `isReloading`, `isLocked`
|
|
17
|
+
- **ResourceRef API**: низкоуровневый доступ к кэшу с поддержкой транзакций
|
|
18
|
+
- **Patch транзакции**: изменения с возможностью commit/abort
|
|
19
|
+
- **Lifecycle хуки**: `onCacheEntryAdded`, `onQueryStarted`
|
|
20
|
+
- **resetAllQueriesCache()**: сброс всего кэша
|
|
21
|
+
|
|
22
|
+
### React
|
|
23
|
+
|
|
24
|
+
- **useResourceRef**: хук для работы с ResourceRef
|
|
25
|
+
|
|
26
|
+
### Devtools
|
|
27
|
+
|
|
28
|
+
- **BatchStrategy**: настройка стратегии обновлений (`'sync'`, `'microtask'`, `'task'`)
|
|
29
|
+
|
|
30
|
+
### Конфигурация
|
|
31
|
+
|
|
32
|
+
- **DefaultOptions**: расширенная конфигурация (`onQueryError`, `getScopeName`)
|