@leancodepl/react-query-cqrs-client 8.5.0 → 8.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -0
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +281 -0
- package/index.cjs.mjs +2 -0
- package/index.d.ts +1 -0
- package/index.esm.js +279 -0
- package/package.json +4 -9
- package/src/index.d.ts +1 -0
- package/src/lib/authGuard.d.ts +3 -0
- package/src/lib/mkCqrsClient.d.ts +93 -0
- package/src/lib/types/index.d.ts +2 -0
- package/src/lib/uncapitalizedJSONParse.d.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# @leancodepl/react-query-cqrs-client
|
|
2
|
+
|
|
3
|
+
TanStack Query CQRS client with hooks for queries, operations, and commands.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **TanStack Query integration** - Built-in caching, optimistic updates, and background refetching
|
|
8
|
+
- **CQRS pattern** - Separate queries, commands, and operations with proper typing
|
|
9
|
+
- **Custom hooks** - Hook factories for all operation types with loading states
|
|
10
|
+
- **Error handling** - Validation errors with custom error codes and handlers
|
|
11
|
+
- **Authentication** - Token handling with automatic refresh integration
|
|
12
|
+
- **Cache management** - Smart invalidation and query dependency management
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @leancodepl/react-query-cqrs-client
|
|
18
|
+
# or
|
|
19
|
+
yarn add @leancodepl/react-query-cqrs-client
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## API
|
|
23
|
+
|
|
24
|
+
### `mkCqrsClient(cqrsEndpoint, queryClient, tokenProvider, ajaxOptions, tokenHeader)`
|
|
25
|
+
|
|
26
|
+
Creates TanStack Query CQRS client with hooks for queries, operations, and commands.
|
|
27
|
+
|
|
28
|
+
**Parameters:**
|
|
29
|
+
|
|
30
|
+
- `cqrsEndpoint: string` - Base URL for CQRS API endpoints
|
|
31
|
+
- `queryClient: QueryClient` - TanStack Query client instance
|
|
32
|
+
- `tokenProvider?: Partial<TokenProvider>` - Optional token provider for authentication
|
|
33
|
+
- `ajaxOptions?: Omit<AjaxConfig, ...>` - Optional RxJS Ajax configuration options
|
|
34
|
+
- `tokenHeader?: string` - Header name for authentication token (default: "Authorization")
|
|
35
|
+
|
|
36
|
+
**Returns:** Object with `createQuery`, `createOperation`, and `createCommand` hook factories
|
|
37
|
+
|
|
38
|
+
## Usage Examples
|
|
39
|
+
|
|
40
|
+
### Basic Setup
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { mkCqrsClient } from "@leancodepl/react-query-cqrs-client"
|
|
44
|
+
import { QueryClient } from "@tanstack/react-query"
|
|
45
|
+
|
|
46
|
+
const queryClient = new QueryClient()
|
|
47
|
+
|
|
48
|
+
const client = mkCqrsClient({
|
|
49
|
+
cqrsEndpoint: "https://api.example.com",
|
|
50
|
+
queryClient,
|
|
51
|
+
tokenProvider: {
|
|
52
|
+
getToken: () => Promise.resolve(localStorage.getItem("token")),
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Query Hook
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import React from 'react';
|
|
61
|
+
|
|
62
|
+
interface GetUserQuery {
|
|
63
|
+
userId: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface UserResult {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
email: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const useGetUser = client.createQuery<GetUserQuery, UserResult>('GetUser');
|
|
73
|
+
|
|
74
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
75
|
+
const { data, isLoading, error } = useGetUser({ userId });
|
|
76
|
+
|
|
77
|
+
if (isLoading) return <div>Loading...</div>;
|
|
78
|
+
if (error) return <div>Error loading user</div>;
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div>
|
|
82
|
+
<h1>{data?.name}</h1>
|
|
83
|
+
<p>{data?.email}</p>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Command Hook
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import React from 'react';
|
|
93
|
+
|
|
94
|
+
interface CreateUserCommand {
|
|
95
|
+
name: string;
|
|
96
|
+
email: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const errorCodes = { EmailExists: 1, InvalidEmail: 2 } as const;
|
|
100
|
+
const useCreateUser = client.createCommand<CreateUserCommand, typeof errorCodes>('CreateUser', errorCodes);
|
|
101
|
+
|
|
102
|
+
function CreateUserForm() {
|
|
103
|
+
const { mutate: createUser, isPending } = useCreateUser({
|
|
104
|
+
handler: (handle) =>
|
|
105
|
+
handle('success', () => 'User created successfully')
|
|
106
|
+
.handle('EmailExists', () => 'Email already exists')
|
|
107
|
+
.handle('failure', () => 'Failed to create user')
|
|
108
|
+
.check(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const handleSubmit = () => {
|
|
112
|
+
createUser({ name: 'John', email: 'john@example.com' });
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<button onClick={handleSubmit} disabled={isPending}>
|
|
117
|
+
{isPending ? 'Creating...' : 'Create User'}
|
|
118
|
+
</button>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Operation Hook
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
interface UploadFileOperation {
|
|
127
|
+
file: File;
|
|
128
|
+
folder: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface UploadResult {
|
|
132
|
+
url: string;
|
|
133
|
+
filename: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const useUploadFile = client.createOperation<UploadFileOperation, UploadResult>('UploadFile');
|
|
137
|
+
|
|
138
|
+
function FileUploader() {
|
|
139
|
+
const { mutate: uploadFile, isPending } = useUploadFile({
|
|
140
|
+
invalidateQueries: [['GetFiles']],
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const handleUpload = (file: File) => {
|
|
144
|
+
uploadFile({ file, folder: 'documents' });
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<input
|
|
149
|
+
type="file"
|
|
150
|
+
onChange={(e) => e.target.files?.[0] && handleUpload(e.target.files[0])}
|
|
151
|
+
disabled={isPending}
|
|
152
|
+
/>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exports._default = require('./index.cjs.js').default;
|
package/index.cjs.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactQuery = require('@tanstack/react-query');
|
|
4
|
+
var rxjs = require('rxjs');
|
|
5
|
+
var ajax = require('rxjs/ajax');
|
|
6
|
+
var operators = require('rxjs/operators');
|
|
7
|
+
var validation = require('@leancodepl/validation');
|
|
8
|
+
var utils = require('@leancodepl/utils');
|
|
9
|
+
|
|
10
|
+
function _extends() {
|
|
11
|
+
_extends = Object.assign || function assign(target) {
|
|
12
|
+
for(var i = 1; i < arguments.length; i++){
|
|
13
|
+
var source = arguments[i];
|
|
14
|
+
for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
|
|
15
|
+
}
|
|
16
|
+
return target;
|
|
17
|
+
};
|
|
18
|
+
return _extends.apply(this, arguments);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function _object_without_properties_loose(source, excluded) {
|
|
22
|
+
if (source == null) return {};
|
|
23
|
+
var target = {};
|
|
24
|
+
var sourceKeys = Object.keys(source);
|
|
25
|
+
var key, i;
|
|
26
|
+
for(i = 0; i < sourceKeys.length; i++){
|
|
27
|
+
key = sourceKeys[i];
|
|
28
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
29
|
+
target[key] = source[key];
|
|
30
|
+
}
|
|
31
|
+
return target;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function authGuard(tokenProvider) {
|
|
35
|
+
if (!(tokenProvider == null ? void 0 : tokenProvider.invalidateToken)) {
|
|
36
|
+
return (response)=>response;
|
|
37
|
+
}
|
|
38
|
+
return (response)=>response.pipe(rxjs.catchError((error)=>{
|
|
39
|
+
if (error.status === 401) {
|
|
40
|
+
tokenProvider.invalidateToken == null ? void 0 : tokenProvider.invalidateToken.call(tokenProvider);
|
|
41
|
+
}
|
|
42
|
+
return rxjs.throwError(()=>error);
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function uncapitalizedJSONParse(json) {
|
|
47
|
+
return JSON.parse(json, (key, value)=>{
|
|
48
|
+
if (value === null && key !== "") return undefined;
|
|
49
|
+
if (!value || Array.isArray(value) || typeof value !== "object") return value;
|
|
50
|
+
return Object.fromEntries(Object.entries(value).map(([key, value])=>[
|
|
51
|
+
utils.toLowerFirst(key),
|
|
52
|
+
value
|
|
53
|
+
]));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function uncapitalizedParse() {
|
|
58
|
+
return ($source)=>$source.pipe(operators.map(uncapitalizedJSONParse));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates React Query CQRS client with hooks for queries, operations, and commands.
|
|
62
|
+
*
|
|
63
|
+
* Integrates with React Query to provide caching, background updates, and optimistic updates
|
|
64
|
+
* for CQRS operations. Automatically handles authentication, retries, and response transformation
|
|
65
|
+
* with uncapitalized keys.
|
|
66
|
+
*
|
|
67
|
+
* @param cqrsEndpoint - Base URL for CQRS API endpoints
|
|
68
|
+
* @param queryClient - React Query client instance
|
|
69
|
+
* @param tokenProvider - Optional token provider for authentication
|
|
70
|
+
* @param ajaxOptions - Optional RxJS Ajax configuration options
|
|
71
|
+
* @param tokenHeader - Header name for authentication token (default: "Authorization")
|
|
72
|
+
* @returns Object with `createQuery`, `createOperation`, and `createCommand` hook factories
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const client = mkCqrsClient({
|
|
76
|
+
* cqrsEndpoint: 'https://api.example.com',
|
|
77
|
+
* queryClient: new QueryClient()
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/ function mkCqrsClient({ cqrsEndpoint, queryClient, tokenProvider, ajaxOptions, tokenHeader = "Authorization" }) {
|
|
81
|
+
function mkFetcher(endpoint, config = {}) {
|
|
82
|
+
const apiCall = (data, token)=>{
|
|
83
|
+
var _ajaxOptions_withCredentials;
|
|
84
|
+
return ajax.ajax(_extends({}, ajaxOptions, config, {
|
|
85
|
+
headers: {
|
|
86
|
+
[tokenHeader]: token,
|
|
87
|
+
"Content-Type": "application/json"
|
|
88
|
+
},
|
|
89
|
+
url: `${cqrsEndpoint}/${endpoint}`,
|
|
90
|
+
method: "POST",
|
|
91
|
+
body: data,
|
|
92
|
+
withCredentials: (_ajaxOptions_withCredentials = ajaxOptions == null ? void 0 : ajaxOptions.withCredentials) != null ? _ajaxOptions_withCredentials : true
|
|
93
|
+
}));
|
|
94
|
+
};
|
|
95
|
+
const getToken = tokenProvider == null ? void 0 : tokenProvider.getToken;
|
|
96
|
+
const mk$apiCall = (data, token)=>apiCall(data, token).pipe(authGuard(tokenProvider), operators.map((result)=>result.response));
|
|
97
|
+
if (getToken) {
|
|
98
|
+
return (data)=>rxjs.from(getToken()).pipe(operators.mergeMap((token)=>mk$apiCall(data, token)));
|
|
99
|
+
}
|
|
100
|
+
return (data)=>mk$apiCall(data);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
createQuery (type) {
|
|
104
|
+
const fetcher = mkFetcher(`query/${type}`, {
|
|
105
|
+
responseType: "text"
|
|
106
|
+
});
|
|
107
|
+
function useApiQuery(data, options) {
|
|
108
|
+
return reactQuery.useQuery(_extends({
|
|
109
|
+
queryKey: useApiQuery.key(data),
|
|
110
|
+
queryFn: (context)=>rxjs.firstValueFrom(useApiQuery.fetcher(data, context))
|
|
111
|
+
}, options), queryClient);
|
|
112
|
+
}
|
|
113
|
+
useApiQuery.type = type;
|
|
114
|
+
useApiQuery.fetcher = (data, context)=>rxjs.race([
|
|
115
|
+
fetcher(data).pipe(uncapitalizedParse()),
|
|
116
|
+
...(context == null ? void 0 : context.signal) ? [
|
|
117
|
+
rxjs.fromEvent(context.signal, "abort").pipe(operators.mergeMap(()=>rxjs.throwError(()=>new Error("Query aborted"))))
|
|
118
|
+
] : []
|
|
119
|
+
]);
|
|
120
|
+
useApiQuery.fetch = (data, options)=>queryClient.fetchQuery(_extends({
|
|
121
|
+
queryKey: useApiQuery.key(data),
|
|
122
|
+
queryFn: (context)=>rxjs.firstValueFrom(useApiQuery.fetcher(data, context))
|
|
123
|
+
}, options));
|
|
124
|
+
useApiQuery.lazy = function(options = {}) {
|
|
125
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
126
|
+
return reactQuery.useMutation(_extends({
|
|
127
|
+
mutationKey: [
|
|
128
|
+
type
|
|
129
|
+
],
|
|
130
|
+
mutationFn: (variables)=>rxjs.firstValueFrom(useApiQuery.fetcher(variables))
|
|
131
|
+
}, options), queryClient);
|
|
132
|
+
};
|
|
133
|
+
useApiQuery.infinite = function(initialPageData, options) {
|
|
134
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
135
|
+
return reactQuery.useInfiniteQuery(_extends({
|
|
136
|
+
queryKey: [
|
|
137
|
+
type
|
|
138
|
+
],
|
|
139
|
+
queryFn: async (context)=>await rxjs.firstValueFrom(useApiQuery.fetcher(context.pageParam, context)),
|
|
140
|
+
initialPageParam: initialPageData
|
|
141
|
+
}, options), queryClient);
|
|
142
|
+
};
|
|
143
|
+
useApiQuery.key = (query)=>[
|
|
144
|
+
type,
|
|
145
|
+
query
|
|
146
|
+
];
|
|
147
|
+
function setQueryData(queryOrQueryKey, updater) {
|
|
148
|
+
const key = Array.isArray(queryOrQueryKey) ? queryOrQueryKey : useApiQuery.key(queryOrQueryKey);
|
|
149
|
+
return queryClient.setQueryData(key, updater);
|
|
150
|
+
}
|
|
151
|
+
useApiQuery.setQueryData = setQueryData;
|
|
152
|
+
useApiQuery.setQueriesData = (query, updater)=>queryClient.setQueriesData({
|
|
153
|
+
queryKey: useApiQuery.key(query)
|
|
154
|
+
}, updater);
|
|
155
|
+
useApiQuery.getQueryData = (query)=>queryClient.getQueryData(useApiQuery.key(query));
|
|
156
|
+
useApiQuery.getQueriesData = (query)=>queryClient.getQueriesData({
|
|
157
|
+
queryKey: useApiQuery.key(query)
|
|
158
|
+
});
|
|
159
|
+
useApiQuery.prefetch = (data, options)=>queryClient.prefetchQuery(_extends({
|
|
160
|
+
queryKey: useApiQuery.key(data),
|
|
161
|
+
queryFn: (context)=>rxjs.firstValueFrom(useApiQuery.fetcher(data, context))
|
|
162
|
+
}, options));
|
|
163
|
+
useApiQuery.invalidate = (query)=>queryClient.invalidateQueries({
|
|
164
|
+
queryKey: useApiQuery.key(query)
|
|
165
|
+
});
|
|
166
|
+
useApiQuery.cancel = (query)=>queryClient.cancelQueries({
|
|
167
|
+
queryKey: useApiQuery.key(query)
|
|
168
|
+
});
|
|
169
|
+
useApiQuery.optimisticUpdate = async (updater, query = {})=>{
|
|
170
|
+
await useApiQuery.cancel(query);
|
|
171
|
+
const data = useApiQuery.getQueriesData(query);
|
|
172
|
+
useApiQuery.setQueriesData(query, updater);
|
|
173
|
+
return ()=>data.forEach(([key, result])=>queryClient.setQueryData(key, result));
|
|
174
|
+
};
|
|
175
|
+
return useApiQuery;
|
|
176
|
+
},
|
|
177
|
+
createOperation (type) {
|
|
178
|
+
const fetcher = mkFetcher(`operation/${type}`, {
|
|
179
|
+
responseType: "text"
|
|
180
|
+
});
|
|
181
|
+
function useApiOperation(_param = {}) {
|
|
182
|
+
var { onSuccess: onSuccessBase, invalidateQueries } = _param, options = _object_without_properties_loose(_param, [
|
|
183
|
+
"onSuccess",
|
|
184
|
+
"invalidateQueries"
|
|
185
|
+
]);
|
|
186
|
+
return reactQuery.useMutation(_extends({
|
|
187
|
+
mutationKey: useApiOperation.key,
|
|
188
|
+
mutationFn: (variables)=>rxjs.firstValueFrom(useApiOperation.fetcher(variables))
|
|
189
|
+
}, options, {
|
|
190
|
+
async onSuccess (data, variables, context) {
|
|
191
|
+
const result = await (onSuccessBase == null ? void 0 : onSuccessBase(data, variables, context));
|
|
192
|
+
if (invalidateQueries) {
|
|
193
|
+
await Promise.allSettled(invalidateQueries.map((queryKey)=>queryClient.invalidateQueries({
|
|
194
|
+
queryKey
|
|
195
|
+
})));
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
}), queryClient);
|
|
200
|
+
}
|
|
201
|
+
useApiOperation.type = type;
|
|
202
|
+
useApiOperation.key = [
|
|
203
|
+
useApiOperation.type
|
|
204
|
+
];
|
|
205
|
+
useApiOperation.fetcher = (variables)=>fetcher(variables).pipe(uncapitalizedParse());
|
|
206
|
+
return useApiOperation;
|
|
207
|
+
},
|
|
208
|
+
createCommand (type, errorCodes) {
|
|
209
|
+
const fetcher = mkFetcher(`command/${type}`);
|
|
210
|
+
function useApiCommand(_param = {}) {
|
|
211
|
+
var { invalidateQueries, handler, optimisticUpdate, onMutate, onError, onSettled } = _param, options = _object_without_properties_loose(_param, [
|
|
212
|
+
"invalidateQueries",
|
|
213
|
+
"handler",
|
|
214
|
+
"optimisticUpdate",
|
|
215
|
+
"onMutate",
|
|
216
|
+
"onError",
|
|
217
|
+
"onSettled"
|
|
218
|
+
]);
|
|
219
|
+
return reactQuery.useMutation(_extends({}, options, {
|
|
220
|
+
mutationKey: useApiCommand.key,
|
|
221
|
+
mutationFn: (variables)=>rxjs.firstValueFrom(useApiCommand.call(variables, handler)),
|
|
222
|
+
async onMutate (variables) {
|
|
223
|
+
// there's really no good way to do it without type cast
|
|
224
|
+
const baseContext = await (onMutate == null ? void 0 : onMutate(variables));
|
|
225
|
+
var _optimisticUpdate;
|
|
226
|
+
const optimisticUpdateReverts = await Promise.all((_optimisticUpdate = optimisticUpdate == null ? void 0 : optimisticUpdate(variables)) != null ? _optimisticUpdate : []);
|
|
227
|
+
return _extends({}, baseContext, {
|
|
228
|
+
revertOptimisticUpdate: ()=>optimisticUpdateReverts.forEach((revertOptimisticUpdate)=>revertOptimisticUpdate())
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
async onError (error, variables, context) {
|
|
232
|
+
await (onError == null ? void 0 : onError(error, variables, context));
|
|
233
|
+
context == null ? void 0 : context.revertOptimisticUpdate();
|
|
234
|
+
},
|
|
235
|
+
async onSettled (data, error, variables, context) {
|
|
236
|
+
if (invalidateQueries) {
|
|
237
|
+
await Promise.allSettled(invalidateQueries.map((queryKey)=>queryClient.invalidateQueries({
|
|
238
|
+
queryKey
|
|
239
|
+
})));
|
|
240
|
+
}
|
|
241
|
+
return await (onSettled == null ? void 0 : onSettled(data, error, variables, context));
|
|
242
|
+
}
|
|
243
|
+
}), queryClient);
|
|
244
|
+
}
|
|
245
|
+
useApiCommand.type = type;
|
|
246
|
+
useApiCommand.key = [
|
|
247
|
+
useApiCommand.type
|
|
248
|
+
];
|
|
249
|
+
useApiCommand.fetcher = (variables)=>fetcher(variables);
|
|
250
|
+
useApiCommand.call = (variables, handler)=>{
|
|
251
|
+
const $response = useApiCommand.fetcher(variables);
|
|
252
|
+
return $response.pipe(operators.map((result)=>({
|
|
253
|
+
isSuccess: true,
|
|
254
|
+
result
|
|
255
|
+
})), rxjs.catchError((e)=>rxjs.of(useApiCommand.mapError(e))), operators.map((response)=>{
|
|
256
|
+
const result = handler ? useApiCommand.handleResponse(handler)(response) : response;
|
|
257
|
+
if (!response.isSuccess || !response.result.WasSuccessful) {
|
|
258
|
+
throw result;
|
|
259
|
+
}
|
|
260
|
+
return result;
|
|
261
|
+
}));
|
|
262
|
+
};
|
|
263
|
+
useApiCommand.mapError = (e)=>{
|
|
264
|
+
if (e instanceof ajax.AjaxError && e.status === 422) {
|
|
265
|
+
return {
|
|
266
|
+
isSuccess: true,
|
|
267
|
+
result: e.response
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
isSuccess: false,
|
|
272
|
+
error: e
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
useApiCommand.handleResponse = (handler)=>(response)=>handler(validation.handleResponse(response, errorCodes));
|
|
276
|
+
return useApiCommand;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
exports.mkCqrsClient = mkCqrsClient;
|
package/index.cjs.mjs
ADDED
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./src/index";
|
package/index.esm.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { useMutation, useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { catchError, throwError, of, race, fromEvent, from, firstValueFrom } from 'rxjs';
|
|
3
|
+
import { AjaxError, ajax } from 'rxjs/ajax';
|
|
4
|
+
import { map, mergeMap } from 'rxjs/operators';
|
|
5
|
+
import { handleResponse } from '@leancodepl/validation';
|
|
6
|
+
import { toLowerFirst } from '@leancodepl/utils';
|
|
7
|
+
|
|
8
|
+
function _extends() {
|
|
9
|
+
_extends = Object.assign || function assign(target) {
|
|
10
|
+
for(var i = 1; i < arguments.length; i++){
|
|
11
|
+
var source = arguments[i];
|
|
12
|
+
for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
return _extends.apply(this, arguments);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function _object_without_properties_loose(source, excluded) {
|
|
20
|
+
if (source == null) return {};
|
|
21
|
+
var target = {};
|
|
22
|
+
var sourceKeys = Object.keys(source);
|
|
23
|
+
var key, i;
|
|
24
|
+
for(i = 0; i < sourceKeys.length; i++){
|
|
25
|
+
key = sourceKeys[i];
|
|
26
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
27
|
+
target[key] = source[key];
|
|
28
|
+
}
|
|
29
|
+
return target;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function authGuard(tokenProvider) {
|
|
33
|
+
if (!(tokenProvider == null ? void 0 : tokenProvider.invalidateToken)) {
|
|
34
|
+
return (response)=>response;
|
|
35
|
+
}
|
|
36
|
+
return (response)=>response.pipe(catchError((error)=>{
|
|
37
|
+
if (error.status === 401) {
|
|
38
|
+
tokenProvider.invalidateToken == null ? void 0 : tokenProvider.invalidateToken.call(tokenProvider);
|
|
39
|
+
}
|
|
40
|
+
return throwError(()=>error);
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function uncapitalizedJSONParse(json) {
|
|
45
|
+
return JSON.parse(json, (key, value)=>{
|
|
46
|
+
if (value === null && key !== "") return undefined;
|
|
47
|
+
if (!value || Array.isArray(value) || typeof value !== "object") return value;
|
|
48
|
+
return Object.fromEntries(Object.entries(value).map(([key, value])=>[
|
|
49
|
+
toLowerFirst(key),
|
|
50
|
+
value
|
|
51
|
+
]));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function uncapitalizedParse() {
|
|
56
|
+
return ($source)=>$source.pipe(map(uncapitalizedJSONParse));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates React Query CQRS client with hooks for queries, operations, and commands.
|
|
60
|
+
*
|
|
61
|
+
* Integrates with React Query to provide caching, background updates, and optimistic updates
|
|
62
|
+
* for CQRS operations. Automatically handles authentication, retries, and response transformation
|
|
63
|
+
* with uncapitalized keys.
|
|
64
|
+
*
|
|
65
|
+
* @param cqrsEndpoint - Base URL for CQRS API endpoints
|
|
66
|
+
* @param queryClient - React Query client instance
|
|
67
|
+
* @param tokenProvider - Optional token provider for authentication
|
|
68
|
+
* @param ajaxOptions - Optional RxJS Ajax configuration options
|
|
69
|
+
* @param tokenHeader - Header name for authentication token (default: "Authorization")
|
|
70
|
+
* @returns Object with `createQuery`, `createOperation`, and `createCommand` hook factories
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const client = mkCqrsClient({
|
|
74
|
+
* cqrsEndpoint: 'https://api.example.com',
|
|
75
|
+
* queryClient: new QueryClient()
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/ function mkCqrsClient({ cqrsEndpoint, queryClient, tokenProvider, ajaxOptions, tokenHeader = "Authorization" }) {
|
|
79
|
+
function mkFetcher(endpoint, config = {}) {
|
|
80
|
+
const apiCall = (data, token)=>{
|
|
81
|
+
var _ajaxOptions_withCredentials;
|
|
82
|
+
return ajax(_extends({}, ajaxOptions, config, {
|
|
83
|
+
headers: {
|
|
84
|
+
[tokenHeader]: token,
|
|
85
|
+
"Content-Type": "application/json"
|
|
86
|
+
},
|
|
87
|
+
url: `${cqrsEndpoint}/${endpoint}`,
|
|
88
|
+
method: "POST",
|
|
89
|
+
body: data,
|
|
90
|
+
withCredentials: (_ajaxOptions_withCredentials = ajaxOptions == null ? void 0 : ajaxOptions.withCredentials) != null ? _ajaxOptions_withCredentials : true
|
|
91
|
+
}));
|
|
92
|
+
};
|
|
93
|
+
const getToken = tokenProvider == null ? void 0 : tokenProvider.getToken;
|
|
94
|
+
const mk$apiCall = (data, token)=>apiCall(data, token).pipe(authGuard(tokenProvider), map((result)=>result.response));
|
|
95
|
+
if (getToken) {
|
|
96
|
+
return (data)=>from(getToken()).pipe(mergeMap((token)=>mk$apiCall(data, token)));
|
|
97
|
+
}
|
|
98
|
+
return (data)=>mk$apiCall(data);
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
createQuery (type) {
|
|
102
|
+
const fetcher = mkFetcher(`query/${type}`, {
|
|
103
|
+
responseType: "text"
|
|
104
|
+
});
|
|
105
|
+
function useApiQuery(data, options) {
|
|
106
|
+
return useQuery(_extends({
|
|
107
|
+
queryKey: useApiQuery.key(data),
|
|
108
|
+
queryFn: (context)=>firstValueFrom(useApiQuery.fetcher(data, context))
|
|
109
|
+
}, options), queryClient);
|
|
110
|
+
}
|
|
111
|
+
useApiQuery.type = type;
|
|
112
|
+
useApiQuery.fetcher = (data, context)=>race([
|
|
113
|
+
fetcher(data).pipe(uncapitalizedParse()),
|
|
114
|
+
...(context == null ? void 0 : context.signal) ? [
|
|
115
|
+
fromEvent(context.signal, "abort").pipe(mergeMap(()=>throwError(()=>new Error("Query aborted"))))
|
|
116
|
+
] : []
|
|
117
|
+
]);
|
|
118
|
+
useApiQuery.fetch = (data, options)=>queryClient.fetchQuery(_extends({
|
|
119
|
+
queryKey: useApiQuery.key(data),
|
|
120
|
+
queryFn: (context)=>firstValueFrom(useApiQuery.fetcher(data, context))
|
|
121
|
+
}, options));
|
|
122
|
+
useApiQuery.lazy = function(options = {}) {
|
|
123
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
124
|
+
return useMutation(_extends({
|
|
125
|
+
mutationKey: [
|
|
126
|
+
type
|
|
127
|
+
],
|
|
128
|
+
mutationFn: (variables)=>firstValueFrom(useApiQuery.fetcher(variables))
|
|
129
|
+
}, options), queryClient);
|
|
130
|
+
};
|
|
131
|
+
useApiQuery.infinite = function(initialPageData, options) {
|
|
132
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
133
|
+
return useInfiniteQuery(_extends({
|
|
134
|
+
queryKey: [
|
|
135
|
+
type
|
|
136
|
+
],
|
|
137
|
+
queryFn: async (context)=>await firstValueFrom(useApiQuery.fetcher(context.pageParam, context)),
|
|
138
|
+
initialPageParam: initialPageData
|
|
139
|
+
}, options), queryClient);
|
|
140
|
+
};
|
|
141
|
+
useApiQuery.key = (query)=>[
|
|
142
|
+
type,
|
|
143
|
+
query
|
|
144
|
+
];
|
|
145
|
+
function setQueryData(queryOrQueryKey, updater) {
|
|
146
|
+
const key = Array.isArray(queryOrQueryKey) ? queryOrQueryKey : useApiQuery.key(queryOrQueryKey);
|
|
147
|
+
return queryClient.setQueryData(key, updater);
|
|
148
|
+
}
|
|
149
|
+
useApiQuery.setQueryData = setQueryData;
|
|
150
|
+
useApiQuery.setQueriesData = (query, updater)=>queryClient.setQueriesData({
|
|
151
|
+
queryKey: useApiQuery.key(query)
|
|
152
|
+
}, updater);
|
|
153
|
+
useApiQuery.getQueryData = (query)=>queryClient.getQueryData(useApiQuery.key(query));
|
|
154
|
+
useApiQuery.getQueriesData = (query)=>queryClient.getQueriesData({
|
|
155
|
+
queryKey: useApiQuery.key(query)
|
|
156
|
+
});
|
|
157
|
+
useApiQuery.prefetch = (data, options)=>queryClient.prefetchQuery(_extends({
|
|
158
|
+
queryKey: useApiQuery.key(data),
|
|
159
|
+
queryFn: (context)=>firstValueFrom(useApiQuery.fetcher(data, context))
|
|
160
|
+
}, options));
|
|
161
|
+
useApiQuery.invalidate = (query)=>queryClient.invalidateQueries({
|
|
162
|
+
queryKey: useApiQuery.key(query)
|
|
163
|
+
});
|
|
164
|
+
useApiQuery.cancel = (query)=>queryClient.cancelQueries({
|
|
165
|
+
queryKey: useApiQuery.key(query)
|
|
166
|
+
});
|
|
167
|
+
useApiQuery.optimisticUpdate = async (updater, query = {})=>{
|
|
168
|
+
await useApiQuery.cancel(query);
|
|
169
|
+
const data = useApiQuery.getQueriesData(query);
|
|
170
|
+
useApiQuery.setQueriesData(query, updater);
|
|
171
|
+
return ()=>data.forEach(([key, result])=>queryClient.setQueryData(key, result));
|
|
172
|
+
};
|
|
173
|
+
return useApiQuery;
|
|
174
|
+
},
|
|
175
|
+
createOperation (type) {
|
|
176
|
+
const fetcher = mkFetcher(`operation/${type}`, {
|
|
177
|
+
responseType: "text"
|
|
178
|
+
});
|
|
179
|
+
function useApiOperation(_param = {}) {
|
|
180
|
+
var { onSuccess: onSuccessBase, invalidateQueries } = _param, options = _object_without_properties_loose(_param, [
|
|
181
|
+
"onSuccess",
|
|
182
|
+
"invalidateQueries"
|
|
183
|
+
]);
|
|
184
|
+
return useMutation(_extends({
|
|
185
|
+
mutationKey: useApiOperation.key,
|
|
186
|
+
mutationFn: (variables)=>firstValueFrom(useApiOperation.fetcher(variables))
|
|
187
|
+
}, options, {
|
|
188
|
+
async onSuccess (data, variables, context) {
|
|
189
|
+
const result = await (onSuccessBase == null ? void 0 : onSuccessBase(data, variables, context));
|
|
190
|
+
if (invalidateQueries) {
|
|
191
|
+
await Promise.allSettled(invalidateQueries.map((queryKey)=>queryClient.invalidateQueries({
|
|
192
|
+
queryKey
|
|
193
|
+
})));
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
}), queryClient);
|
|
198
|
+
}
|
|
199
|
+
useApiOperation.type = type;
|
|
200
|
+
useApiOperation.key = [
|
|
201
|
+
useApiOperation.type
|
|
202
|
+
];
|
|
203
|
+
useApiOperation.fetcher = (variables)=>fetcher(variables).pipe(uncapitalizedParse());
|
|
204
|
+
return useApiOperation;
|
|
205
|
+
},
|
|
206
|
+
createCommand (type, errorCodes) {
|
|
207
|
+
const fetcher = mkFetcher(`command/${type}`);
|
|
208
|
+
function useApiCommand(_param = {}) {
|
|
209
|
+
var { invalidateQueries, handler, optimisticUpdate, onMutate, onError, onSettled } = _param, options = _object_without_properties_loose(_param, [
|
|
210
|
+
"invalidateQueries",
|
|
211
|
+
"handler",
|
|
212
|
+
"optimisticUpdate",
|
|
213
|
+
"onMutate",
|
|
214
|
+
"onError",
|
|
215
|
+
"onSettled"
|
|
216
|
+
]);
|
|
217
|
+
return useMutation(_extends({}, options, {
|
|
218
|
+
mutationKey: useApiCommand.key,
|
|
219
|
+
mutationFn: (variables)=>firstValueFrom(useApiCommand.call(variables, handler)),
|
|
220
|
+
async onMutate (variables) {
|
|
221
|
+
// there's really no good way to do it without type cast
|
|
222
|
+
const baseContext = await (onMutate == null ? void 0 : onMutate(variables));
|
|
223
|
+
var _optimisticUpdate;
|
|
224
|
+
const optimisticUpdateReverts = await Promise.all((_optimisticUpdate = optimisticUpdate == null ? void 0 : optimisticUpdate(variables)) != null ? _optimisticUpdate : []);
|
|
225
|
+
return _extends({}, baseContext, {
|
|
226
|
+
revertOptimisticUpdate: ()=>optimisticUpdateReverts.forEach((revertOptimisticUpdate)=>revertOptimisticUpdate())
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
async onError (error, variables, context) {
|
|
230
|
+
await (onError == null ? void 0 : onError(error, variables, context));
|
|
231
|
+
context == null ? void 0 : context.revertOptimisticUpdate();
|
|
232
|
+
},
|
|
233
|
+
async onSettled (data, error, variables, context) {
|
|
234
|
+
if (invalidateQueries) {
|
|
235
|
+
await Promise.allSettled(invalidateQueries.map((queryKey)=>queryClient.invalidateQueries({
|
|
236
|
+
queryKey
|
|
237
|
+
})));
|
|
238
|
+
}
|
|
239
|
+
return await (onSettled == null ? void 0 : onSettled(data, error, variables, context));
|
|
240
|
+
}
|
|
241
|
+
}), queryClient);
|
|
242
|
+
}
|
|
243
|
+
useApiCommand.type = type;
|
|
244
|
+
useApiCommand.key = [
|
|
245
|
+
useApiCommand.type
|
|
246
|
+
];
|
|
247
|
+
useApiCommand.fetcher = (variables)=>fetcher(variables);
|
|
248
|
+
useApiCommand.call = (variables, handler)=>{
|
|
249
|
+
const $response = useApiCommand.fetcher(variables);
|
|
250
|
+
return $response.pipe(map((result)=>({
|
|
251
|
+
isSuccess: true,
|
|
252
|
+
result
|
|
253
|
+
})), catchError((e)=>of(useApiCommand.mapError(e))), map((response)=>{
|
|
254
|
+
const result = handler ? useApiCommand.handleResponse(handler)(response) : response;
|
|
255
|
+
if (!response.isSuccess || !response.result.WasSuccessful) {
|
|
256
|
+
throw result;
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}));
|
|
260
|
+
};
|
|
261
|
+
useApiCommand.mapError = (e)=>{
|
|
262
|
+
if (e instanceof AjaxError && e.status === 422) {
|
|
263
|
+
return {
|
|
264
|
+
isSuccess: true,
|
|
265
|
+
result: e.response
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
isSuccess: false,
|
|
270
|
+
error: e
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
useApiCommand.handleResponse = (handler)=>(response)=>handler(handleResponse(response, errorCodes));
|
|
274
|
+
return useApiCommand;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export { mkCqrsClient };
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leancodepl/react-query-cqrs-client",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@leancodepl/cqrs-client-base": "8.
|
|
7
|
-
"@leancodepl/utils": "8.
|
|
8
|
-
"@leancodepl/validation": "8.
|
|
6
|
+
"@leancodepl/cqrs-client-base": "8.6.0",
|
|
7
|
+
"@leancodepl/utils": "8.6.0",
|
|
8
|
+
"@leancodepl/validation": "8.6.0",
|
|
9
9
|
"@tanstack/react-query": ">=5.0.0",
|
|
10
10
|
"rxjs": ">=7.0.0"
|
|
11
11
|
},
|
|
@@ -44,11 +44,6 @@
|
|
|
44
44
|
"name": "LeanCode",
|
|
45
45
|
"url": "https://leancode.co"
|
|
46
46
|
},
|
|
47
|
-
"files": [
|
|
48
|
-
"dist",
|
|
49
|
-
"README.md",
|
|
50
|
-
"CHANGELOG.md"
|
|
51
|
-
],
|
|
52
47
|
"sideEffects": false,
|
|
53
48
|
"exports": {
|
|
54
49
|
"./package.json": "./package.json",
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { mkCqrsClient } from "./lib/mkCqrsClient";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { FetchQueryOptions, InfiniteData, QueryClient, QueryFunctionContext, QueryKey, UndefinedInitialDataInfiniteOptions, UndefinedInitialDataOptions, Updater, UseMutationOptions, UseMutationResult } from "@tanstack/react-query";
|
|
2
|
+
import { Observable, OperatorFunction } from "rxjs";
|
|
3
|
+
import { AjaxConfig } from "rxjs/ajax";
|
|
4
|
+
import { ApiResponse, ApiSuccess, CommandResult, FailedCommandResult, SuccessfulCommandResult, TokenProvider } from "@leancodepl/cqrs-client-base";
|
|
5
|
+
import { ValidationErrorsHandler } from "@leancodepl/validation";
|
|
6
|
+
import { NullableUncapitalizeDeep } from "./types";
|
|
7
|
+
export declare function uncapitalizedParse<TResult>(): OperatorFunction<string, NullableUncapitalizeDeep<TResult>>;
|
|
8
|
+
/**
|
|
9
|
+
* Creates React Query CQRS client with hooks for queries, operations, and commands.
|
|
10
|
+
*
|
|
11
|
+
* Integrates with React Query to provide caching, background updates, and optimistic updates
|
|
12
|
+
* for CQRS operations. Automatically handles authentication, retries, and response transformation
|
|
13
|
+
* with uncapitalized keys.
|
|
14
|
+
*
|
|
15
|
+
* @param cqrsEndpoint - Base URL for CQRS API endpoints
|
|
16
|
+
* @param queryClient - React Query client instance
|
|
17
|
+
* @param tokenProvider - Optional token provider for authentication
|
|
18
|
+
* @param ajaxOptions - Optional RxJS Ajax configuration options
|
|
19
|
+
* @param tokenHeader - Header name for authentication token (default: "Authorization")
|
|
20
|
+
* @returns Object with `createQuery`, `createOperation`, and `createCommand` hook factories
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const client = mkCqrsClient({
|
|
24
|
+
* cqrsEndpoint: 'https://api.example.com',
|
|
25
|
+
* queryClient: new QueryClient()
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function mkCqrsClient({ cqrsEndpoint, queryClient, tokenProvider, ajaxOptions, tokenHeader, }: {
|
|
30
|
+
cqrsEndpoint: string;
|
|
31
|
+
queryClient: QueryClient;
|
|
32
|
+
tokenProvider?: Partial<TokenProvider>;
|
|
33
|
+
ajaxOptions?: Omit<AjaxConfig, "body" | "headers" | "method" | "responseType" | "url">;
|
|
34
|
+
tokenHeader?: string;
|
|
35
|
+
}): {
|
|
36
|
+
createQuery<TQuery, TResult>(type: string): {
|
|
37
|
+
(data: TQuery, options?: Omit<UndefinedInitialDataOptions<NullableUncapitalizeDeep<TResult>, unknown>, "queryFn" | "queryKey">): import("@tanstack/react-query").UseQueryResult<NullableUncapitalizeDeep<TResult>, unknown>;
|
|
38
|
+
type: string;
|
|
39
|
+
fetcher(data: TQuery, context?: QueryFunctionContext<QueryKey>): Observable<NullableUncapitalizeDeep<TResult>>;
|
|
40
|
+
fetch(data: TQuery, options?: Omit<FetchQueryOptions<NullableUncapitalizeDeep<TResult>, unknown>, "queryFn" | "queryKey">): Promise<NullableUncapitalizeDeep<TResult>>;
|
|
41
|
+
lazy<TContext = unknown>(options?: Omit<UseMutationOptions<NullableUncapitalizeDeep<TResult>, unknown, TQuery, TContext>, "mutationFn" | "mutationKey">): UseMutationResult<NullableUncapitalizeDeep<TResult>, unknown, TQuery, TContext>;
|
|
42
|
+
infinite(initialPageData: TQuery, options: Omit<UndefinedInitialDataInfiniteOptions<NullableUncapitalizeDeep<TResult>, unknown, InfiniteData<NullableUncapitalizeDeep<TResult>>, QueryKey, TQuery>, "initialPageParam" | "queryFn" | "queryKey" | "select">): import("@tanstack/react-query").UseInfiniteQueryResult<InfiniteData<NullableUncapitalizeDeep<TResult>, TQuery>, unknown>;
|
|
43
|
+
key(query: Partial<TQuery>): readonly [string, Partial<TQuery>];
|
|
44
|
+
setQueryData: {
|
|
45
|
+
(query: TQuery, updater: Updater<NullableUncapitalizeDeep<TResult> | undefined, NullableUncapitalizeDeep<TResult> | undefined>): NullableUncapitalizeDeep<TResult> | undefined;
|
|
46
|
+
(queryKey: QueryKey, updater: Updater<NullableUncapitalizeDeep<TResult> | undefined, NullableUncapitalizeDeep<TResult> | undefined>): NullableUncapitalizeDeep<TResult> | undefined;
|
|
47
|
+
};
|
|
48
|
+
setQueriesData(query: Partial<TQuery>, updater: Updater<NullableUncapitalizeDeep<TResult> | undefined, NullableUncapitalizeDeep<TResult> | undefined>): [readonly unknown[], unknown][];
|
|
49
|
+
getQueryData(query: TQuery): NullableUncapitalizeDeep<TResult> | undefined;
|
|
50
|
+
getQueriesData(query: Partial<TQuery>): [readonly unknown[], NullableUncapitalizeDeep<TResult> | undefined][];
|
|
51
|
+
prefetch(data: TQuery, options?: Omit<FetchQueryOptions<NullableUncapitalizeDeep<TResult>, unknown>, "initialData" | "queryFn" | "queryKey">): Promise<void>;
|
|
52
|
+
invalidate(query: Partial<TQuery>): Promise<void>;
|
|
53
|
+
cancel(query: Partial<TQuery>): Promise<void>;
|
|
54
|
+
optimisticUpdate(updater: Updater<NullableUncapitalizeDeep<TResult> | undefined, NullableUncapitalizeDeep<TResult> | undefined>, query?: Partial<TQuery>): Promise<() => void>;
|
|
55
|
+
};
|
|
56
|
+
createOperation<TOperation, TResult>(type: string): {
|
|
57
|
+
<TContext = unknown>({ onSuccess: onSuccessBase, invalidateQueries, ...options }?: Omit<UseMutationOptions<NullableUncapitalizeDeep<TResult>, unknown, TOperation, TContext>, "mutationFn" | "mutationKey"> & {
|
|
58
|
+
invalidateQueries?: QueryKey[];
|
|
59
|
+
}): UseMutationResult<NullableUncapitalizeDeep<TResult>, unknown, TOperation, TContext>;
|
|
60
|
+
type: string;
|
|
61
|
+
key: string[];
|
|
62
|
+
fetcher(variables: TOperation): Observable<NullableUncapitalizeDeep<TResult>>;
|
|
63
|
+
};
|
|
64
|
+
createCommand<TCommand, TErrorCodes extends {
|
|
65
|
+
[name: string]: number;
|
|
66
|
+
}>(type: string, errorCodes: TErrorCodes): {
|
|
67
|
+
<TContext extends Record<string, unknown> = {}>(options?: Omit<UseMutationOptions<ApiSuccess<SuccessfulCommandResult>, ApiResponse<FailedCommandResult<TErrorCodes>>, TCommand, TContext>, "mutationFn" | "mutationKey"> & {
|
|
68
|
+
invalidateQueries?: QueryKey[];
|
|
69
|
+
handler?: undefined;
|
|
70
|
+
optimisticUpdate?: (variables: TCommand) => Promise<() => void>[];
|
|
71
|
+
}): UseMutationResult<ApiSuccess<SuccessfulCommandResult>, ApiResponse<FailedCommandResult<TErrorCodes>>, TCommand, TContext>;
|
|
72
|
+
<TResult, TContext extends Record<string, unknown> = {}>(options?: Omit<UseMutationOptions<TResult, TResult, TCommand, TContext>, "mutationFn" | "mutationKey"> & {
|
|
73
|
+
invalidateQueries?: QueryKey[];
|
|
74
|
+
handler: (handler: ValidationErrorsHandler<TErrorCodes & {
|
|
75
|
+
success: -1;
|
|
76
|
+
failure: -2;
|
|
77
|
+
}, never>) => TResult;
|
|
78
|
+
optimisticUpdate?: (variables: TCommand) => Promise<() => void>[];
|
|
79
|
+
}): UseMutationResult<TResult, TResult, TCommand, TContext>;
|
|
80
|
+
type: string;
|
|
81
|
+
key: string[];
|
|
82
|
+
fetcher(variables: TCommand): Observable<SuccessfulCommandResult>;
|
|
83
|
+
call<TResult>(variables: TCommand, handler?: (handler: ValidationErrorsHandler<TErrorCodes & {
|
|
84
|
+
success: -1;
|
|
85
|
+
failure: -2;
|
|
86
|
+
}, never>) => TResult): Observable<ApiSuccess<SuccessfulCommandResult> | TResult>;
|
|
87
|
+
mapError(e: unknown): ApiResponse<CommandResult<TErrorCodes>>;
|
|
88
|
+
handleResponse<TResult>(handler: (handler: ValidationErrorsHandler<TErrorCodes & {
|
|
89
|
+
success: -1;
|
|
90
|
+
failure: -2;
|
|
91
|
+
}, never>) => TResult): (response: ApiResponse<CommandResult<TErrorCodes>>) => TResult;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uncapitalizedJSONParse(json: string): any;
|