@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
package/README.md
CHANGED
|
@@ -27,14 +27,15 @@ RxToolkit решает эти проблемы, предоставляя сво
|
|
|
27
27
|
- 🔧 **Framework-agnostic** — Стройте систему и описывайте логику в изолированном месте.
|
|
28
28
|
- ⚡ **Built on RxJS** — Наследует всю мощь RxJS.
|
|
29
29
|
- 💾 **Кеш-менеджер** — Предоставляет Query реализацию для работы с данными.
|
|
30
|
-
- 🔷 **TypeScript-first** — Полная
|
|
31
|
-
- 🔗 **Интеграция с фреймворками** — Как и RxJS напрямую работает в Angular, Svelte и SolidJS.
|
|
32
|
-
|
|
30
|
+
- 🔷 **TypeScript-first** — Полная типизация.
|
|
31
|
+
- 🔗 **Интеграция с фреймворками** — Как и RxJS напрямую работает в Angular, Svelte и SolidJS.
|
|
32
|
+
Поставляется с React-хуками из коробки.
|
|
33
33
|
|
|
34
34
|
## 📚 Документация
|
|
35
35
|
- [**RxSignals**](./docs/signals/README.md) - реактивные примитивы
|
|
36
36
|
- [**RxQuery**](./docs/query/README.md) - кеш-менеджер для работы с данными
|
|
37
37
|
- [**React**](./docs/usage/react/README.md) - интеграция с React
|
|
38
|
+
- [**Devtools**](./docs/devtools/README.md) - инструменты разработчика
|
|
38
39
|
|
|
39
40
|
## 🌟 Примеры
|
|
40
41
|
|
|
@@ -59,20 +60,26 @@ count$ = toSignal(store.count$);
|
|
|
59
60
|
// Angular pipe
|
|
60
61
|
{{ store.count$ | async }}
|
|
61
62
|
|
|
63
|
+
// SolidJS
|
|
64
|
+
const count$ = from(store.count$)
|
|
65
|
+
|
|
62
66
|
// Svelte
|
|
63
67
|
$: count = store.count$;
|
|
64
68
|
```
|
|
65
69
|
|
|
66
70
|
###### Работаем с RxJS
|
|
71
|
+
|
|
67
72
|
```typescript
|
|
68
73
|
// Создаем Observable
|
|
74
|
+
|
|
69
75
|
const clicker$ = fromEvent(document, 'click').pipe(
|
|
70
76
|
debounceTime(300),
|
|
71
|
-
scan(count => count + 1, 0)
|
|
77
|
+
scan(count => count + 1, 0),
|
|
78
|
+
startWith(0),
|
|
72
79
|
);
|
|
73
80
|
|
|
74
81
|
// Получаем сигнал из Observable
|
|
75
|
-
const clickCount$ =
|
|
82
|
+
const clickCount$ = signalize(clicker$);
|
|
76
83
|
const doubled$ = new Computed(() => clickCount$.value * 2);
|
|
77
84
|
|
|
78
85
|
console.log(doubled$.value); // Всегда актуальное значение
|
|
@@ -84,7 +91,7 @@ const on10click$ = doubled$.pipe(
|
|
|
84
91
|
);
|
|
85
92
|
|
|
86
93
|
on10click$.subscribe(() => {
|
|
87
|
-
console.log('Great! That you first reached 10 clicks');
|
|
94
|
+
console.log('Great! That you first reached 10 clicks!');
|
|
88
95
|
});
|
|
89
96
|
|
|
90
97
|
```
|
|
@@ -92,40 +99,39 @@ on10click$.subscribe(() => {
|
|
|
92
99
|
###### RxQuery (Корзина покупок)
|
|
93
100
|
```tsx
|
|
94
101
|
const getCart = createResource({
|
|
95
|
-
|
|
102
|
+
queryFn: fetchCart,
|
|
96
103
|
});
|
|
97
104
|
|
|
98
105
|
const toggleCardItem = createOperation({
|
|
99
|
-
queryFn:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
queryFn: fetchToggleCardItem,
|
|
107
|
+
link(add) {
|
|
108
|
+
add({
|
|
109
|
+
resource: getCart,
|
|
110
|
+
forwardArgs: () => undefined,
|
|
111
|
+
optimisticUpdate: ({ draft, data, args }) => {
|
|
112
|
+
const item = draft.items.find(i => i.id === data.id);
|
|
113
|
+
if (!item) return;
|
|
114
|
+
item.enabled = args.enabled;
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
111
118
|
});
|
|
112
119
|
|
|
113
120
|
function ShoppingCart() {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
);
|
|
121
|
+
const cartQuery = useResourceAgent(getCart);
|
|
122
|
+
const [toggleItem] = useOperationAgent(toggleCardItem);
|
|
123
|
+
const cart = cartQuery.data;
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<Container isLoading={cartQuery.isLoading}>
|
|
127
|
+
{cart?.items.map(item => (
|
|
128
|
+
<CartItem
|
|
129
|
+
key={item.id}
|
|
130
|
+
item={item}
|
|
131
|
+
onToggle={() => toggleItem({ id: item.id, enabled: !item.enabled })}
|
|
132
|
+
/>
|
|
133
|
+
))}
|
|
134
|
+
</Container>
|
|
135
|
+
);
|
|
130
136
|
}
|
|
131
137
|
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Batcher } from "../signals";
|
|
2
|
+
export function reduxDevtools() {
|
|
3
|
+
let state = {};
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
const reduxDevtools = window.__REDUX_DEVTOOLS_EXTENSION__.connect({ name: 'RxToolkit' });
|
|
6
|
+
reduxDevtools.init(state);
|
|
7
|
+
const scheduler = Batcher.scheduler(Infinity);
|
|
8
|
+
const updateFn = () => {
|
|
9
|
+
reduxDevtools.send({ type: 'update' }, state);
|
|
10
|
+
};
|
|
11
|
+
const createFn = () => {
|
|
12
|
+
reduxDevtools.send({ type: 'create' }, state);
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
state(name, initState) {
|
|
16
|
+
state = { ...state, [name]: initState };
|
|
17
|
+
scheduler.schedule(createFn);
|
|
18
|
+
return (newState) => {
|
|
19
|
+
state = { ...state, [name]: newState };
|
|
20
|
+
scheduler.schedule(updateFn);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DevtoolsLike } from "../../query/types/devtools";
|
|
2
2
|
type Update = Partial<{
|
|
3
|
-
DEVTOOLS:
|
|
3
|
+
DEVTOOLS: DevtoolsLike | null;
|
|
4
4
|
onError: (error: unknown) => void;
|
|
5
5
|
}>;
|
|
6
6
|
export declare class DefaultOptions {
|
|
@@ -50,7 +50,7 @@ class OperationQueryState {
|
|
|
50
50
|
}
|
|
51
51
|
export class Operation {
|
|
52
52
|
_options;
|
|
53
|
-
_queriesCache = new QueriesCache('
|
|
53
|
+
_queriesCache = new QueriesCache('Operation');
|
|
54
54
|
_links = [];
|
|
55
55
|
constructor(_options) {
|
|
56
56
|
this._options = _options;
|
|
@@ -89,7 +89,7 @@ export class Operation {
|
|
|
89
89
|
state.unlocker = ref.lock();
|
|
90
90
|
}
|
|
91
91
|
if (link.optimisticUpdate && ref.has) {
|
|
92
|
-
state.patch = ref.
|
|
92
|
+
state.patch = ref.patch((draft) => {
|
|
93
93
|
return link.optimisticUpdate({ draft, args });
|
|
94
94
|
});
|
|
95
95
|
}
|
|
@@ -102,7 +102,7 @@ export class Operation {
|
|
|
102
102
|
/**
|
|
103
103
|
* Обновляем связанные ресурсы
|
|
104
104
|
*/
|
|
105
|
-
linksMeta.forEach(({ link, ref }) => {
|
|
105
|
+
linksMeta.forEach(({ link, ref, state }) => {
|
|
106
106
|
if (link.update && ref.has) {
|
|
107
107
|
ref.update((draft) => {
|
|
108
108
|
return link.update({ draft, args, data });
|
|
@@ -111,6 +111,7 @@ export class Operation {
|
|
|
111
111
|
if (link.create && !ref.has) {
|
|
112
112
|
ref.create(link.create({ args, data }));
|
|
113
113
|
}
|
|
114
|
+
state.patch?.commit();
|
|
114
115
|
});
|
|
115
116
|
})
|
|
116
117
|
.catch((error) => {
|
|
@@ -120,7 +121,7 @@ export class Operation {
|
|
|
120
121
|
* Обновляем связанные ресурсы
|
|
121
122
|
*/
|
|
122
123
|
linksMeta.forEach(({ state }) => {
|
|
123
|
-
state.patch?.
|
|
124
|
+
state.patch?.abort();
|
|
124
125
|
});
|
|
125
126
|
})
|
|
126
127
|
.finally(() => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Computed } from "../../../signal";
|
|
2
1
|
import { OperationAgentInstanse, OperationDefinition } from "../../../query/types/Operation.types";
|
|
2
|
+
import { Computed } from "../../../signals";
|
|
3
3
|
import type { Operation } from "./Operation";
|
|
4
4
|
export declare class OperationAgent<D extends OperationDefinition> implements OperationAgentInstanse<D> {
|
|
5
5
|
private _operation;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Computed, Signal } from "../../../
|
|
1
|
+
import { Computed, Signal } from "../../../signals";
|
|
2
2
|
export class OperationAgent {
|
|
3
3
|
_operation;
|
|
4
4
|
_operations$ = new Signal({
|
|
5
5
|
current$: null,
|
|
6
|
-
});
|
|
6
|
+
}, { disableDevtools: true });
|
|
7
7
|
state$ = new Computed(() => {
|
|
8
8
|
const operations = this._operations$.value;
|
|
9
9
|
const currState = operations.current$?.value;
|
|
@@ -28,7 +28,7 @@ export class OperationAgent {
|
|
|
28
28
|
data: currState.data ?? undefined,
|
|
29
29
|
args: currState.arg,
|
|
30
30
|
};
|
|
31
|
-
});
|
|
31
|
+
}, { disableDevtools: true });
|
|
32
32
|
constructor(_operation) {
|
|
33
33
|
this._operation = _operation;
|
|
34
34
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IndirectMap } from "../../query/lib/IndirectMap";
|
|
2
2
|
import { ReactiveCache } from "../../query/lib/ReactiveCache";
|
|
3
|
+
import { Indexer } from "../../signals/base/Indexer";
|
|
3
4
|
import { SharedOptions } from "./SharedOptions";
|
|
4
5
|
export class QueriesCache {
|
|
5
6
|
_logname;
|
|
@@ -14,12 +15,17 @@ export class QueriesCache {
|
|
|
14
15
|
const cache = new ReactiveCache({
|
|
15
16
|
initialState,
|
|
16
17
|
});
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
19
|
-
const
|
|
20
|
-
|
|
18
|
+
const stateDevtools = SharedOptions.DEVTOOLS?.state;
|
|
19
|
+
if (stateDevtools) {
|
|
20
|
+
const key = `${this._logname}:${JSON.stringify(args)}:i=${Indexer.getIndex()}`;
|
|
21
|
+
let devtools = stateDevtools(key, initialState);
|
|
21
22
|
cache.spy$.subscribe((state) => {
|
|
22
|
-
|
|
23
|
+
if (state === initialState)
|
|
24
|
+
return;
|
|
25
|
+
devtools(state);
|
|
26
|
+
});
|
|
27
|
+
cache.onClean$.subscribe(() => {
|
|
28
|
+
devtools('$CLEANED');
|
|
23
29
|
});
|
|
24
30
|
}
|
|
25
31
|
cache.onClean$.subscribe(() => {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ReactiveCache } from "../../../query/lib/ReactiveCache";
|
|
2
|
-
import type { ResourceCreateOptions, ResourceDefinition, ResourceInstance, ResourceRefInstanse } from "../../../query/types/Resource.types";
|
|
2
|
+
import type { ResourceCreateOptions, ResourceDefinition, ResourceInstance, ResourceRefInstanse, ResourceTransaction } from "../../../query/types/Resource.types";
|
|
3
3
|
import { QueriesCache } from "../QueriesCache";
|
|
4
4
|
import { ResourceAgent } from "./ResourceAgent";
|
|
5
5
|
export type CoreResourceQueryState<D extends ResourceDefinition> = {
|
|
6
|
+
transactions: ResourceTransaction[] | null;
|
|
6
7
|
abortController: AbortController | null;
|
|
7
8
|
args: D['Args'] | null;
|
|
9
|
+
savedData: D['Data'] | null;
|
|
8
10
|
data: D['Data'] | null;
|
|
9
11
|
error: unknown | null;
|
|
10
12
|
isError: boolean;
|
|
@@ -31,7 +33,17 @@ export declare class Resource<D extends ResourceDefinition> implements ResourceI
|
|
|
31
33
|
decrementLock(args: D['Args'], options?: {
|
|
32
34
|
cache?: CoreResourceQueryCache<D>;
|
|
33
35
|
}): CoreResourceQueryCache<D> | null;
|
|
34
|
-
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated
|
|
38
|
+
*/
|
|
39
|
+
updateData_legacy(args: D['Args'], updateFn: (data: D['Data']) => D['Data'], options?: {
|
|
40
|
+
cache?: CoreResourceQueryCache<D>;
|
|
41
|
+
}): CoreResourceQueryCache<D> | null;
|
|
42
|
+
update(args: D['Args'], updateFn: (data: D['Data'], savedData: D['Data'] | null, transactions: ResourceTransaction[] | null) => {
|
|
43
|
+
data: D['Data'];
|
|
44
|
+
transactions: ResourceTransaction[] | null;
|
|
45
|
+
savedData: D['Data'] | null;
|
|
46
|
+
}, options?: {
|
|
35
47
|
cache?: CoreResourceQueryCache<D>;
|
|
36
48
|
}): CoreResourceQueryCache<D> | null;
|
|
37
49
|
initiate(args: D['Args'], options?: {
|
|
@@ -5,6 +5,8 @@ import { ResourceRef } from "./ResourceRef";
|
|
|
5
5
|
class ResourceQueryState {
|
|
6
6
|
static create() {
|
|
7
7
|
return {
|
|
8
|
+
transactions: null,
|
|
9
|
+
savedData: null,
|
|
8
10
|
abortController: null,
|
|
9
11
|
args: null,
|
|
10
12
|
data: null,
|
|
@@ -31,6 +33,8 @@ class ResourceQueryState {
|
|
|
31
33
|
static success(state, data) {
|
|
32
34
|
return {
|
|
33
35
|
...state,
|
|
36
|
+
savedData: null,
|
|
37
|
+
transactions: null,
|
|
34
38
|
data,
|
|
35
39
|
isLoading: false,
|
|
36
40
|
isReloading: false,
|
|
@@ -67,16 +71,28 @@ class ResourceQueryState {
|
|
|
67
71
|
lockCount
|
|
68
72
|
};
|
|
69
73
|
}
|
|
74
|
+
static update(state, data, savedData, transactions) {
|
|
75
|
+
return {
|
|
76
|
+
...state,
|
|
77
|
+
transactions,
|
|
78
|
+
savedData,
|
|
79
|
+
data
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* @deprecated
|
|
84
|
+
*/
|
|
70
85
|
static setData(state, data) {
|
|
71
86
|
return {
|
|
72
87
|
...state,
|
|
88
|
+
transactions: null,
|
|
73
89
|
data
|
|
74
90
|
};
|
|
75
91
|
}
|
|
76
92
|
}
|
|
77
93
|
export class Resource {
|
|
78
94
|
_options;
|
|
79
|
-
_queriesCache = new QueriesCache('
|
|
95
|
+
_queriesCache = new QueriesCache('Resource');
|
|
80
96
|
constructor(_options) {
|
|
81
97
|
this._options = _options;
|
|
82
98
|
}
|
|
@@ -108,7 +124,10 @@ export class Resource {
|
|
|
108
124
|
cache.next(ResourceQueryState.decrementLock(cache.value));
|
|
109
125
|
return cache;
|
|
110
126
|
}
|
|
111
|
-
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated
|
|
129
|
+
*/
|
|
130
|
+
updateData_legacy(args, updateFn, options) {
|
|
112
131
|
let cache = options?.cache ?? this.getQueryCache(args);
|
|
113
132
|
if (!cache) {
|
|
114
133
|
return null;
|
|
@@ -121,6 +140,19 @@ export class Resource {
|
|
|
121
140
|
cache.next(ResourceQueryState.setData(cache.value, newData));
|
|
122
141
|
return cache;
|
|
123
142
|
}
|
|
143
|
+
update(args, updateFn, options) {
|
|
144
|
+
let cache = options?.cache ?? this.getQueryCache(args);
|
|
145
|
+
if (!cache) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const cacheValue = cache.value;
|
|
149
|
+
if (!cacheValue.isDone) {
|
|
150
|
+
return cache;
|
|
151
|
+
}
|
|
152
|
+
const { data, transactions, savedData } = updateFn(cacheValue.data, cacheValue.savedData, cacheValue.transactions);
|
|
153
|
+
cache.next(ResourceQueryState.update(cache.value, data, savedData, transactions));
|
|
154
|
+
return cache;
|
|
155
|
+
}
|
|
124
156
|
initiate(args, options) {
|
|
125
157
|
let cache = options?.cache ?? this._queriesCache.getQueryCache(args);
|
|
126
158
|
const state = ResourceQueryState.load(cache?.value, args);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Computed } from "../../../
|
|
1
|
+
import { Computed } from "../../../signals";
|
|
2
2
|
import { ResourceAgentInstance, ResourceDefinition } from "../../../query/types/Resource.types";
|
|
3
3
|
import type { Resource } from "./Resource";
|
|
4
4
|
export declare class ResourceAgent<D extends ResourceDefinition> implements ResourceAgentInstance<D> {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Computed, Signal } from "../../../
|
|
1
|
+
import { Computed, Signal } from "../../../signals";
|
|
2
2
|
export class ResourceAgent {
|
|
3
3
|
_resource;
|
|
4
4
|
_resources$ = new Signal({
|
|
5
5
|
previous$: null,
|
|
6
6
|
current$: null,
|
|
7
|
-
});
|
|
7
|
+
}, { disableDevtools: true });
|
|
8
8
|
state$ = new Computed(() => {
|
|
9
9
|
const resources = this._resources$.value;
|
|
10
10
|
let prevState;
|
|
@@ -41,7 +41,7 @@ export class ResourceAgent {
|
|
|
41
41
|
data: isShowPrev ? prevState.data ?? undefined : currState.data ?? undefined,
|
|
42
42
|
args: currState.args ?? undefined,
|
|
43
43
|
};
|
|
44
|
-
});
|
|
44
|
+
}, { disableDevtools: true });
|
|
45
45
|
constructor(_resource) {
|
|
46
46
|
this._resource = _resource;
|
|
47
47
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Resource } from "./Resource";
|
|
2
|
-
import { ResourceDefinition, ResourceRefInstanse } from "../../types/Resource.types";
|
|
2
|
+
import { ResourceDefinition, ResourceRefInstanse, ResourceTransaction } from "../../types/Resource.types";
|
|
3
3
|
export declare class ResourceRef<D extends ResourceDefinition> implements ResourceRefInstanse<D> {
|
|
4
4
|
private readonly _resource;
|
|
5
5
|
private readonly _args;
|
|
@@ -13,6 +13,7 @@ export declare class ResourceRef<D extends ResourceDefinition> implements Resour
|
|
|
13
13
|
update(updateFn: (data: D["Data"]) => D["Data"]): {
|
|
14
14
|
rollback: () => void;
|
|
15
15
|
};
|
|
16
|
+
patch(patchFn: (data: D["Data"]) => void): ResourceTransaction | null;
|
|
16
17
|
create(data: D["Data"]): void;
|
|
17
18
|
invalidate(): void;
|
|
18
19
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { applyPatches, enablePatches, produceWithPatches } from "immer";
|
|
2
|
+
enablePatches();
|
|
1
3
|
export class ResourceRef {
|
|
2
4
|
_resource;
|
|
3
5
|
_args;
|
|
@@ -36,13 +38,105 @@ export class ResourceRef {
|
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
40
|
const value = cacheItem.value;
|
|
39
|
-
this._resource.
|
|
41
|
+
this._resource.updateData_legacy(this._args, updateFn, { cache: cacheItem });
|
|
40
42
|
return {
|
|
41
43
|
rollback: () => {
|
|
42
|
-
this._resource.
|
|
44
|
+
this._resource.updateData_legacy(this._args, () => value.data, { cache: cacheItem });
|
|
43
45
|
}
|
|
44
46
|
};
|
|
45
47
|
}
|
|
48
|
+
patch(patchFn) {
|
|
49
|
+
let isSkipped = true;
|
|
50
|
+
const reapplyFn = (data, savedData, transactions) => {
|
|
51
|
+
if (!transactions || transactions.length === 0) {
|
|
52
|
+
return { data, transactions, savedData };
|
|
53
|
+
}
|
|
54
|
+
// Все валидные committed - пропускаем и убираем из очереди
|
|
55
|
+
// Все aborted - применяем и убираем из очереди
|
|
56
|
+
// Все pending - применяем и оставляем в очереди
|
|
57
|
+
// Все commited (которые после pending) - применяем, но оставляем в очереди
|
|
58
|
+
// Все aborted (которые после pending) - откатываем, но оставляем в очереди
|
|
59
|
+
// Те после применения всех транзакций, очередь должна начинаться с первой pending транзакции (если есть), включая все, что после неё.
|
|
60
|
+
let newSavedData = savedData ?? data;
|
|
61
|
+
let currentData = savedData ?? data;
|
|
62
|
+
const remainingTransactions = [];
|
|
63
|
+
let foundPending = false;
|
|
64
|
+
transactions.forEach((transaction) => {
|
|
65
|
+
if (transaction.status === 'pending') {
|
|
66
|
+
foundPending = true;
|
|
67
|
+
// Применяем pending транзакцию и оставляем в очереди
|
|
68
|
+
currentData = applyPatches(currentData, transaction.patches);
|
|
69
|
+
remainingTransactions.push(transaction);
|
|
70
|
+
}
|
|
71
|
+
else if (foundPending) {
|
|
72
|
+
// После pending транзакции
|
|
73
|
+
if (transaction.status === 'committed') {
|
|
74
|
+
// Применяем и оставляем в очереди
|
|
75
|
+
currentData = applyPatches(currentData, transaction.patches);
|
|
76
|
+
remainingTransactions.push(transaction);
|
|
77
|
+
}
|
|
78
|
+
else if (transaction.status === 'aborted') {
|
|
79
|
+
// Откатываем и оставляем в очереди
|
|
80
|
+
currentData = applyPatches(currentData, transaction.inversePatches);
|
|
81
|
+
remainingTransactions.push(transaction);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// До первой pending транзакции
|
|
86
|
+
if (transaction.status === 'committed') {
|
|
87
|
+
// Применяем и убираем из очереди
|
|
88
|
+
const patches = transaction.patches;
|
|
89
|
+
currentData = applyPatches(currentData, patches);
|
|
90
|
+
newSavedData = currentData;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const hasTransactions = remainingTransactions.length > 0;
|
|
95
|
+
return {
|
|
96
|
+
data: currentData,
|
|
97
|
+
transactions: hasTransactions ? remainingTransactions : null,
|
|
98
|
+
savedData: hasTransactions ? newSavedData : null,
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const reapplyTransactions = () => {
|
|
102
|
+
this._resource.update(this._args, reapplyFn, { cache: this._cacheItem ?? undefined });
|
|
103
|
+
};
|
|
104
|
+
const transaction = {
|
|
105
|
+
patches: [],
|
|
106
|
+
inversePatches: [],
|
|
107
|
+
status: 'pending',
|
|
108
|
+
abort() {
|
|
109
|
+
if (this.status !== 'pending')
|
|
110
|
+
return;
|
|
111
|
+
this.status = 'aborted';
|
|
112
|
+
reapplyTransactions();
|
|
113
|
+
},
|
|
114
|
+
commit() {
|
|
115
|
+
if (this.status !== 'pending')
|
|
116
|
+
return;
|
|
117
|
+
this.status = 'committed';
|
|
118
|
+
reapplyTransactions();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const updateFn = (data, savedData, transactions) => {
|
|
122
|
+
isSkipped = false;
|
|
123
|
+
const [newData, patches, inversePatches] = produceWithPatches(data, (draft) => patchFn(draft));
|
|
124
|
+
transaction.patches = patches;
|
|
125
|
+
transaction.inversePatches = inversePatches;
|
|
126
|
+
const newTransactions = [...(transactions ?? [])];
|
|
127
|
+
newTransactions.push(transaction);
|
|
128
|
+
return {
|
|
129
|
+
data: newData,
|
|
130
|
+
transactions: newTransactions,
|
|
131
|
+
savedData: savedData ?? data,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
this._cacheItem = this._resource.update(this._args, updateFn, { cache: this._cacheItem ?? undefined });
|
|
135
|
+
if (isSkipped) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return transaction;
|
|
139
|
+
}
|
|
46
140
|
create(data) {
|
|
47
141
|
throw new Error("Method not implemented.");
|
|
48
142
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DevtoolsLike } from "../../query/types/devtools";
|
|
2
2
|
export declare class SharedOptions {
|
|
3
|
-
static DEVTOOLS:
|
|
3
|
+
static DEVTOOLS: DevtoolsLike | null;
|
|
4
4
|
static onError: ((error: unknown) => void) | null;
|
|
5
5
|
}
|
package/dist/query/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './api/createResource';
|
|
2
2
|
export * from './api/createOperation';
|
|
3
3
|
export * from './api/DefaultOptions';
|
|
4
|
-
export * from './api/createDevtools';
|
|
5
4
|
export * from './SKIP_TOKEN';
|
|
6
5
|
export * from './react/useResourceAgent';
|
|
6
|
+
export * from './react/useResourceRef';
|
|
7
7
|
export * from './react/useOperationAgent';
|
package/dist/query/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './api/createResource';
|
|
2
2
|
export * from './api/createOperation';
|
|
3
3
|
export * from './api/DefaultOptions';
|
|
4
|
-
export * from './api/createDevtools';
|
|
5
4
|
export * from './SKIP_TOKEN';
|
|
6
5
|
export * from './react/useResourceAgent';
|
|
6
|
+
export * from './react/useResourceRef';
|
|
7
7
|
export * from './react/useOperationAgent';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { finalize, ReplaySubject, share, Subject, takeUntil, timer } from "rxjs";
|
|
2
|
-
import { Signal } from "../../
|
|
2
|
+
import { Signal } from "../../signals";
|
|
3
3
|
/**
|
|
4
4
|
* Класс `ReactiveCache` представляет собой реактивный кэш,
|
|
5
5
|
* который позволяет управлять состоянием и временем жизни кэшированных данных.
|
|
@@ -44,7 +44,7 @@ export class ReactiveCache {
|
|
|
44
44
|
*/
|
|
45
45
|
constructor(options) {
|
|
46
46
|
this._cacheLifeTime = options.cacheLifeTime || 60_000;
|
|
47
|
-
this._state$ = new Signal(options.initialState);
|
|
47
|
+
this._state$ = new Signal(options.initialState, { disableDevtools: true });
|
|
48
48
|
this._value = options.initialState;
|
|
49
49
|
this.spy$ = this._state$.pipe(takeUntil(this.onClean$));
|
|
50
50
|
this.value$ = this._state$.pipe(finalize(() => {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SKIP } from "../../query/SKIP_TOKEN";
|
|
2
|
+
import { ResourceDefinition, ResourceInstance, ResourceRefInstanse } from "../../query/types/Resource.types";
|
|
3
|
+
import { Prettify } from "../../query/types/shared.types";
|
|
4
|
+
type Result<D extends ResourceDefinition> = Prettify<ResourceRefInstanse<D>> | null;
|
|
5
|
+
export declare function useResourceRef<D extends ResourceDefinition>(res: ResourceInstance<D>, ...argss: D['Args'] extends void ? [] | [typeof SKIP] : [D['Args'] | typeof SKIP]): Result<D>;
|
|
6
|
+
export {};
|