@confect/react 10.0.0-next.8 → 10.0.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/CHANGELOG.md +753 -14
- package/README.md +3 -1
- package/dist/OptimisticLocalStore.d.ts.map +1 -1
- package/dist/PaginatedQueryResult.d.ts +123 -0
- package/dist/PaginatedQueryResult.d.ts.map +1 -0
- package/dist/PaginatedQueryResult.js +91 -0
- package/dist/PaginatedQueryResult.js.map +1 -0
- package/dist/QueryResult.d.ts +18 -15
- package/dist/QueryResult.d.ts.map +1 -1
- package/dist/QueryResult.js +15 -32
- package/dist/QueryResult.js.map +1 -1
- package/dist/StreamPagination.d.ts +159 -0
- package/dist/StreamPagination.d.ts.map +1 -0
- package/dist/StreamPagination.js +182 -0
- package/dist/StreamPagination.js.map +1 -0
- package/dist/index.d.ts +76 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +222 -10
- package/dist/index.js.map +1 -1
- package/package.json +26 -24
- package/src/PaginatedQueryResult.ts +321 -0
- package/src/QueryResult.ts +52 -49
- package/src/StreamPagination.ts +403 -0
- package/src/index.ts +522 -9
- package/dist/tsconfig.src.tsbuildinfo +0 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import * as Equal from "effect/Equal";
|
|
2
|
+
import * as Function from "effect/Function";
|
|
3
|
+
import * as Hash from "effect/Hash";
|
|
4
|
+
import * as Match from "effect/Match";
|
|
5
|
+
import * as Pipeable from "effect/Pipeable";
|
|
6
|
+
import * as Predicate from "effect/Predicate";
|
|
7
|
+
|
|
8
|
+
const TypeId = "~@confect/react/PaginatedQueryResult";
|
|
9
|
+
type TypeId = typeof TypeId;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A `PaginatedQueryResult` represents the result of a Confect paginated query.
|
|
13
|
+
* The non-`Failure` variants mirror the statuses of `usePaginatedQuery` from
|
|
14
|
+
* `convex/react` and all carry `results` and `isLoading`, so the common UI
|
|
15
|
+
* shape—render the list, show a spinner while loading—needs only an
|
|
16
|
+
* {@link isFailure} early-out and plain field access.
|
|
17
|
+
*
|
|
18
|
+
* Every variant carries `results`, including `Failure`: when a later page
|
|
19
|
+
* fails, the pages already loaded are still worth rendering alongside the
|
|
20
|
+
* error, so they are not discarded. `Failure` additionally carries the query's
|
|
21
|
+
* decoded typed error.
|
|
22
|
+
*
|
|
23
|
+
* `loadMore` appears only on `CanLoadMore`, the one state it can make progress
|
|
24
|
+
* from. The underlying Convex hook exposes it on every status, but calling it
|
|
25
|
+
* while a page is in flight, once the list is exhausted, or after a failure is
|
|
26
|
+
* an intentional no-op there; narrowing to `CanLoadMore`—via
|
|
27
|
+
* {@link isCanLoadMore} or {@link match}—makes that statically apparent instead
|
|
28
|
+
* of silently dropping the call.
|
|
29
|
+
*
|
|
30
|
+
* When the query declares no `error` schema (`E` is `never`), the `Failure`
|
|
31
|
+
* variant is excluded from the type entirely, so `isLoading` is accessible
|
|
32
|
+
* without any narrowing.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam Item - The type of a decoded page item.
|
|
35
|
+
* @typeParam E - The type of the decoded typed error in the `Failure` variant.
|
|
36
|
+
*/
|
|
37
|
+
export type PaginatedQueryResult<Item, E = never> = [E] extends [never]
|
|
38
|
+
?
|
|
39
|
+
| LoadingFirstPage<Item, E>
|
|
40
|
+
| LoadingMore<Item, E>
|
|
41
|
+
| CanLoadMore<Item, E>
|
|
42
|
+
| Exhausted<Item, E>
|
|
43
|
+
:
|
|
44
|
+
| LoadingFirstPage<Item, E>
|
|
45
|
+
| LoadingMore<Item, E>
|
|
46
|
+
| CanLoadMore<Item, E>
|
|
47
|
+
| Exhausted<Item, E>
|
|
48
|
+
| Failure<Item, E>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Every variant regardless of `E`—the parameter type for guards and
|
|
52
|
+
* {@link match}, since `PaginatedQueryResult` itself excludes `Failure` when `E`
|
|
53
|
+
* is `never`.
|
|
54
|
+
*/
|
|
55
|
+
export type Variants<Item, E = never> =
|
|
56
|
+
| LoadingFirstPage<Item, E>
|
|
57
|
+
| LoadingMore<Item, E>
|
|
58
|
+
| CanLoadMore<Item, E>
|
|
59
|
+
| Exhausted<Item, E>
|
|
60
|
+
| Failure<Item, E>;
|
|
61
|
+
|
|
62
|
+
export declare namespace PaginatedQueryResult {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
64
|
+
export interface Proto<out Item, out E> extends Pipeable.Pipeable {
|
|
65
|
+
readonly [TypeId]: TypeId;
|
|
66
|
+
readonly "~Item": Item;
|
|
67
|
+
readonly "~E": E;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type Item<R> = R extends { readonly "~Item": infer Item_ }
|
|
71
|
+
? Item_
|
|
72
|
+
: never;
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
75
|
+
export type Failure<R> = R extends { readonly "~E": infer E } ? E : never;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface LoadingFirstPage<
|
|
79
|
+
Item,
|
|
80
|
+
E = never,
|
|
81
|
+
> extends PaginatedQueryResult.Proto<Item, E> {
|
|
82
|
+
readonly _tag: "LoadingFirstPage";
|
|
83
|
+
readonly skipped: boolean;
|
|
84
|
+
readonly results: ReadonlyArray<Item>;
|
|
85
|
+
readonly isLoading: true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface LoadingMore<
|
|
89
|
+
Item,
|
|
90
|
+
E = never,
|
|
91
|
+
> extends PaginatedQueryResult.Proto<Item, E> {
|
|
92
|
+
readonly _tag: "LoadingMore";
|
|
93
|
+
readonly results: ReadonlyArray<Item>;
|
|
94
|
+
readonly isLoading: true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CanLoadMore<
|
|
98
|
+
Item,
|
|
99
|
+
E = never,
|
|
100
|
+
> extends PaginatedQueryResult.Proto<Item, E> {
|
|
101
|
+
readonly _tag: "CanLoadMore";
|
|
102
|
+
readonly results: ReadonlyArray<Item>;
|
|
103
|
+
readonly isLoading: false;
|
|
104
|
+
readonly loadMore: (numItems: number) => void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface Exhausted<Item, E = never> extends PaginatedQueryResult.Proto<
|
|
108
|
+
Item,
|
|
109
|
+
E
|
|
110
|
+
> {
|
|
111
|
+
readonly _tag: "Exhausted";
|
|
112
|
+
readonly results: ReadonlyArray<Item>;
|
|
113
|
+
readonly isLoading: false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface Failure<Item, E = never> extends PaginatedQueryResult.Proto<
|
|
117
|
+
Item,
|
|
118
|
+
E
|
|
119
|
+
> {
|
|
120
|
+
readonly _tag: "Failure";
|
|
121
|
+
readonly error: E;
|
|
122
|
+
/**
|
|
123
|
+
* The pages loaded before the failure.
|
|
124
|
+
*/
|
|
125
|
+
readonly results: ReadonlyArray<Item>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const isPaginatedQueryResult = (
|
|
129
|
+
u: unknown,
|
|
130
|
+
): u is Variants<unknown, unknown> => Predicate.hasProperty(u, TypeId);
|
|
131
|
+
|
|
132
|
+
const PaginatedQueryResultProto = {
|
|
133
|
+
[TypeId]: TypeId,
|
|
134
|
+
pipe(this: Variants<any, any>, ...args: ReadonlyArray<unknown>) {
|
|
135
|
+
return Pipeable.pipeArguments(
|
|
136
|
+
this,
|
|
137
|
+
args as unknown as Parameters<typeof Pipeable.pipeArguments>[1],
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
/**
|
|
141
|
+
* `CanLoadMore`'s `loadMore` is excluded from equality and hashing, like
|
|
142
|
+
* function fields generally: two results that render identically are equal
|
|
143
|
+
* even though their callbacks differ by identity.
|
|
144
|
+
*/
|
|
145
|
+
[Equal.symbol](this: Variants<any, any>, that: Variants<any, any>): boolean {
|
|
146
|
+
if (this._tag !== that._tag) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return Match.value(this).pipe(
|
|
150
|
+
Match.tag(
|
|
151
|
+
"LoadingFirstPage",
|
|
152
|
+
(self) => self.skipped === (that as LoadingFirstPage<any, any>).skipped,
|
|
153
|
+
),
|
|
154
|
+
Match.tag("LoadingMore", "CanLoadMore", "Exhausted", (self) =>
|
|
155
|
+
Equal.equals(self.results, that.results),
|
|
156
|
+
),
|
|
157
|
+
Match.tag(
|
|
158
|
+
"Failure",
|
|
159
|
+
(self) =>
|
|
160
|
+
Equal.equals(self.error, (that as Failure<any, any>).error) &&
|
|
161
|
+
Equal.equals(self.results, (that as Failure<any, any>).results),
|
|
162
|
+
),
|
|
163
|
+
Match.exhaustive,
|
|
164
|
+
);
|
|
165
|
+
},
|
|
166
|
+
[Hash.symbol](this: Variants<any, any>): number {
|
|
167
|
+
const tagHash = Hash.string(this._tag);
|
|
168
|
+
return Match.value(this).pipe(
|
|
169
|
+
Match.tag("LoadingFirstPage", (self) =>
|
|
170
|
+
Hash.combine(tagHash)(Hash.hash(self.skipped)),
|
|
171
|
+
),
|
|
172
|
+
Match.tag("LoadingMore", "CanLoadMore", "Exhausted", (self) =>
|
|
173
|
+
Hash.combine(tagHash)(Hash.hash(self.results)),
|
|
174
|
+
),
|
|
175
|
+
Match.tag("Failure", (self) =>
|
|
176
|
+
Hash.combine(tagHash)(
|
|
177
|
+
Hash.combine(Hash.hash(self.error))(Hash.hash(self.results)),
|
|
178
|
+
),
|
|
179
|
+
),
|
|
180
|
+
Match.exhaustive,
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const noResults: ReadonlyArray<never> = [];
|
|
186
|
+
|
|
187
|
+
export const loadingFirstPage = <Item = never, E = never>(options: {
|
|
188
|
+
skipped: boolean;
|
|
189
|
+
}): LoadingFirstPage<Item, E> =>
|
|
190
|
+
Object.assign(Object.create(PaginatedQueryResultProto), {
|
|
191
|
+
_tag: "LoadingFirstPage" as const,
|
|
192
|
+
skipped: options.skipped,
|
|
193
|
+
results: noResults,
|
|
194
|
+
isLoading: true as const,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
export const loadingMore = <Item, E = never>(options: {
|
|
198
|
+
results: ReadonlyArray<Item>;
|
|
199
|
+
}): LoadingMore<Item, E> =>
|
|
200
|
+
Object.assign(Object.create(PaginatedQueryResultProto), {
|
|
201
|
+
_tag: "LoadingMore" as const,
|
|
202
|
+
results: options.results,
|
|
203
|
+
isLoading: true as const,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
export const canLoadMore = <Item, E = never>(options: {
|
|
207
|
+
results: ReadonlyArray<Item>;
|
|
208
|
+
loadMore: (numItems: number) => void;
|
|
209
|
+
}): CanLoadMore<Item, E> =>
|
|
210
|
+
Object.assign(Object.create(PaginatedQueryResultProto), {
|
|
211
|
+
_tag: "CanLoadMore" as const,
|
|
212
|
+
results: options.results,
|
|
213
|
+
isLoading: false as const,
|
|
214
|
+
loadMore: options.loadMore,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
export const exhausted = <Item, E = never>(options: {
|
|
218
|
+
results: ReadonlyArray<Item>;
|
|
219
|
+
}): Exhausted<Item, E> =>
|
|
220
|
+
Object.assign(Object.create(PaginatedQueryResultProto), {
|
|
221
|
+
_tag: "Exhausted" as const,
|
|
222
|
+
results: options.results,
|
|
223
|
+
isLoading: false as const,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
export const failure = <E, Item = never>(options: {
|
|
227
|
+
error: E;
|
|
228
|
+
results: ReadonlyArray<Item>;
|
|
229
|
+
}): Failure<Item, E> =>
|
|
230
|
+
Object.assign(Object.create(PaginatedQueryResultProto), {
|
|
231
|
+
_tag: "Failure" as const,
|
|
232
|
+
error: options.error,
|
|
233
|
+
results: options.results,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
export const isLoadingFirstPage = <Item, E>(
|
|
237
|
+
result: Variants<Item, E>,
|
|
238
|
+
): result is LoadingFirstPage<Item, E> => result._tag === "LoadingFirstPage";
|
|
239
|
+
|
|
240
|
+
export const isLoadingMore = <Item, E>(
|
|
241
|
+
result: Variants<Item, E>,
|
|
242
|
+
): result is LoadingMore<Item, E> => result._tag === "LoadingMore";
|
|
243
|
+
|
|
244
|
+
export const isCanLoadMore = <Item, E>(
|
|
245
|
+
result: Variants<Item, E>,
|
|
246
|
+
): result is CanLoadMore<Item, E> => result._tag === "CanLoadMore";
|
|
247
|
+
|
|
248
|
+
export const isExhausted = <Item, E>(
|
|
249
|
+
result: Variants<Item, E>,
|
|
250
|
+
): result is Exhausted<Item, E> => result._tag === "Exhausted";
|
|
251
|
+
|
|
252
|
+
export const isFailure = <Item, E>(
|
|
253
|
+
result: Variants<Item, E>,
|
|
254
|
+
): result is Failure<Item, E> => result._tag === "Failure";
|
|
255
|
+
|
|
256
|
+
export const isLoading = <Item, E>(
|
|
257
|
+
result: Variants<Item, E>,
|
|
258
|
+
): result is LoadingFirstPage<Item, E> | LoadingMore<Item, E> =>
|
|
259
|
+
result._tag === "LoadingFirstPage" || result._tag === "LoadingMore";
|
|
260
|
+
|
|
261
|
+
type MatchOptions<Item, E, V, W, X, Y, Z> = {
|
|
262
|
+
readonly onLoadingFirstPage: (skipped: boolean) => V;
|
|
263
|
+
readonly onLoadingMore: (results: ReadonlyArray<Item>) => W;
|
|
264
|
+
readonly onCanLoadMore: (
|
|
265
|
+
results: ReadonlyArray<Item>,
|
|
266
|
+
loadMore: (numItems: number) => void,
|
|
267
|
+
) => X;
|
|
268
|
+
readonly onExhausted: (results: ReadonlyArray<Item>) => Y;
|
|
269
|
+
} & ([E] extends [never]
|
|
270
|
+
? {}
|
|
271
|
+
: {
|
|
272
|
+
readonly onFailure: (error: E, results: ReadonlyArray<Item>) => Z;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
type MatchReturns<E, V, W, X, Y, Z> = [E] extends [never]
|
|
276
|
+
? V | W | X | Y
|
|
277
|
+
: V | W | X | Y | Z;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Matches a {@link PaginatedQueryResult} to the appropriate handler based on its
|
|
281
|
+
* tag. If the provided result cannot fail (i.e. `E` is `never`), `onFailure` is
|
|
282
|
+
* not required.
|
|
283
|
+
*/
|
|
284
|
+
export const match: {
|
|
285
|
+
<Item, E, V, W, X, Y, Z = never>(
|
|
286
|
+
options: MatchOptions<Item, E, V, W, X, Y, Z>,
|
|
287
|
+
): (self: Variants<Item, E>) => MatchReturns<E, V, W, X, Y, Z>;
|
|
288
|
+
<Item, E, V, W, X, Y, Z = never>(
|
|
289
|
+
self: Variants<Item, E>,
|
|
290
|
+
options: MatchOptions<Item, E, V, W, X, Y, Z>,
|
|
291
|
+
): MatchReturns<E, V, W, X, Y, Z>;
|
|
292
|
+
} = Function.dual(
|
|
293
|
+
2,
|
|
294
|
+
<Item, E, V, W, X, Y, Z = never>(
|
|
295
|
+
self: Variants<Item, E>,
|
|
296
|
+
options: MatchOptions<Item, E, V, W, X, Y, Z>,
|
|
297
|
+
): MatchReturns<E, V, W, X, Y, Z> =>
|
|
298
|
+
Match.value(self).pipe(
|
|
299
|
+
Match.tag("LoadingFirstPage", (loadingFirstPage_) =>
|
|
300
|
+
options.onLoadingFirstPage(loadingFirstPage_.skipped),
|
|
301
|
+
),
|
|
302
|
+
Match.tag("LoadingMore", (loadingMore_) =>
|
|
303
|
+
options.onLoadingMore(loadingMore_.results),
|
|
304
|
+
),
|
|
305
|
+
Match.tag("CanLoadMore", (canLoadMore_) =>
|
|
306
|
+
options.onCanLoadMore(canLoadMore_.results, canLoadMore_.loadMore),
|
|
307
|
+
),
|
|
308
|
+
Match.tag("Exhausted", (exhausted_) =>
|
|
309
|
+
options.onExhausted(exhausted_.results),
|
|
310
|
+
),
|
|
311
|
+
Match.tag("Failure", (failure_) => {
|
|
312
|
+
if (Predicate.hasProperty(options, "onFailure")) {
|
|
313
|
+
return options.onFailure(failure_.error, failure_.results);
|
|
314
|
+
}
|
|
315
|
+
throw new Error(
|
|
316
|
+
"`onFailure` is required when error schema is provided",
|
|
317
|
+
);
|
|
318
|
+
}),
|
|
319
|
+
Match.exhaustive,
|
|
320
|
+
) as MatchReturns<E, V, W, X, Y, Z>,
|
|
321
|
+
);
|
package/src/QueryResult.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { identity } from "effect/Function";
|
|
2
1
|
import * as Equal from "effect/Equal";
|
|
3
2
|
import * as Function from "effect/Function";
|
|
4
3
|
import * as Hash from "effect/Hash";
|
|
4
|
+
import * as Match from "effect/Match";
|
|
5
5
|
import * as Pipeable from "effect/Pipeable";
|
|
6
6
|
import * as Predicate from "effect/Predicate";
|
|
7
7
|
|
|
8
|
-
const TypeId = "
|
|
8
|
+
const TypeId = "~@confect/react/QueryResult";
|
|
9
9
|
type TypeId = typeof TypeId;
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* A `QueryResult` represents the result of a Confect query.
|
|
13
13
|
*
|
|
14
|
-
* @typeParam A - The type of the decoded `returns` value in the `Success`
|
|
14
|
+
* @typeParam A - The type of the decoded `returns` value in the `Success`
|
|
15
|
+
* variant.
|
|
15
16
|
* @typeParam E - The type of the decoded typed error in the `Failure` variant.
|
|
16
17
|
*/
|
|
17
18
|
export type QueryResult<A, E = never> =
|
|
@@ -20,18 +21,17 @@ export type QueryResult<A, E = never> =
|
|
|
20
21
|
| Failure<A, E>;
|
|
21
22
|
|
|
22
23
|
export declare namespace QueryResult {
|
|
23
|
-
export interface Proto<A, E> extends Pipeable.Pipeable {
|
|
24
|
-
readonly [TypeId]:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
24
|
+
export interface Proto<out A, out E> extends Pipeable.Pipeable {
|
|
25
|
+
readonly [TypeId]: TypeId;
|
|
26
|
+
readonly "~A": A;
|
|
27
|
+
readonly "~E": E;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
//
|
|
31
|
-
export type Success<R> = R extends
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
31
|
+
export type Success<R> = R extends { readonly "~A": infer A } ? A : never;
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
export type Failure<R> = R extends
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
34
|
+
export type Failure<R> = R extends { readonly "~E": infer E } ? E : never;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export interface Loading<A, E = never> extends QueryResult.Proto<A, E> {
|
|
@@ -53,10 +53,7 @@ export const isQueryResult = (u: unknown): u is QueryResult<unknown, unknown> =>
|
|
|
53
53
|
Predicate.hasProperty(u, TypeId);
|
|
54
54
|
|
|
55
55
|
const QueryResultProto = {
|
|
56
|
-
[TypeId]:
|
|
57
|
-
E: identity,
|
|
58
|
-
A: identity,
|
|
59
|
-
},
|
|
56
|
+
[TypeId]: TypeId,
|
|
60
57
|
pipe(this: QueryResult<any, any>, ...args: ReadonlyArray<unknown>) {
|
|
61
58
|
return Pipeable.pipeArguments(
|
|
62
59
|
this,
|
|
@@ -70,25 +67,34 @@ const QueryResultProto = {
|
|
|
70
67
|
if (this._tag !== that._tag) {
|
|
71
68
|
return false;
|
|
72
69
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
return Match.value(this).pipe(
|
|
71
|
+
Match.tag(
|
|
72
|
+
"Loading",
|
|
73
|
+
(self) => self.skipped === (that as Loading<any, any>).skipped,
|
|
74
|
+
),
|
|
75
|
+
Match.tag("Success", (self) =>
|
|
76
|
+
Equal.equals(self.value, (that as Success<any, any>).value),
|
|
77
|
+
),
|
|
78
|
+
Match.tag("Failure", (self) =>
|
|
79
|
+
Equal.equals(self.error, (that as Failure<any, any>).error),
|
|
80
|
+
),
|
|
81
|
+
Match.exhaustive,
|
|
82
|
+
);
|
|
81
83
|
},
|
|
82
84
|
[Hash.symbol](this: QueryResult<any, any>): number {
|
|
83
85
|
const tagHash = Hash.string(this._tag);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
return Match.value(this).pipe(
|
|
87
|
+
Match.tag("Loading", (self) =>
|
|
88
|
+
Hash.combine(tagHash)(Hash.hash(self.skipped)),
|
|
89
|
+
),
|
|
90
|
+
Match.tag("Success", (self) =>
|
|
91
|
+
Hash.combine(tagHash)(Hash.hash(self.value)),
|
|
92
|
+
),
|
|
93
|
+
Match.tag("Failure", (self) =>
|
|
94
|
+
Hash.combine(tagHash)(Hash.hash(self.error)),
|
|
95
|
+
),
|
|
96
|
+
Match.exhaustive,
|
|
97
|
+
);
|
|
92
98
|
},
|
|
93
99
|
};
|
|
94
100
|
|
|
@@ -135,12 +141,11 @@ type MatchReturns<E, X, Y, Z> = [E] extends [never] ? X | Y : X | Y | Z;
|
|
|
135
141
|
* not required.
|
|
136
142
|
*
|
|
137
143
|
* @example
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* });
|
|
144
|
+
* const result = QueryResult.match(queryResult, {
|
|
145
|
+
* onLoading: (skipped) => (skipped ? null : <p>Loading…</p>),
|
|
146
|
+
* onSuccess: (value) => <p>{value.text}</p>,
|
|
147
|
+
* onFailure: (error) => <p>Error: {error.message}</p>,
|
|
148
|
+
* });
|
|
144
149
|
*/
|
|
145
150
|
export const match: {
|
|
146
151
|
<A, E, X, Y, Z = never>(
|
|
@@ -155,20 +160,18 @@ export const match: {
|
|
|
155
160
|
<A, E, X, Y, Z = never>(
|
|
156
161
|
self: QueryResult<A, E>,
|
|
157
162
|
options: MatchOptions<A, E, X, Y, Z>,
|
|
158
|
-
): MatchReturns<E, X, Y, Z> =>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return options.onSuccess(self.value);
|
|
164
|
-
case "Failure": {
|
|
163
|
+
): MatchReturns<E, X, Y, Z> =>
|
|
164
|
+
Match.value(self).pipe(
|
|
165
|
+
Match.tag("Loading", (loading) => options.onLoading(loading.skipped)),
|
|
166
|
+
Match.tag("Success", (success) => options.onSuccess(success.value)),
|
|
167
|
+
Match.tag("Failure", (failure) => {
|
|
165
168
|
if (Predicate.hasProperty(options, "onFailure")) {
|
|
166
|
-
return options.onFailure(
|
|
169
|
+
return options.onFailure(failure.error);
|
|
167
170
|
}
|
|
168
171
|
throw new Error(
|
|
169
172
|
"`onFailure` is required when error schema is provided",
|
|
170
173
|
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
}),
|
|
175
|
+
Match.exhaustive,
|
|
176
|
+
) as MatchReturns<E, X, Y, Z>,
|
|
174
177
|
);
|