@raf-query/core 0.0.18 → 0.1.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 +22 -1
- package/README.md +101 -32
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/cache.d.mts +140 -0
- package/dist/cache.mjs +176 -0
- package/dist/entry.d.mts +113 -0
- package/dist/entry.mjs +421 -0
- package/dist/hydration.d.mts +37 -13
- package/dist/hydration.mjs +78 -17
- package/dist/index.d.mts +8 -7
- package/dist/index.mjs +6 -6
- package/dist/infinite.d.mts +5 -0
- package/dist/infinite.mjs +59 -0
- package/dist/listen.mjs +19 -0
- package/dist/observer.d.mts +59 -40
- package/dist/observer.mjs +215 -133
- package/dist/options.d.mts +50 -14
- package/dist/options.mjs +3 -0
- package/dist/platform.d.mts +27 -0
- package/dist/platform.mjs +44 -0
- package/dist/query.d.mts +80 -51
- package/dist/query.mjs +111 -421
- package/dist/request.mjs +47 -0
- package/dist/retry.mjs +25 -0
- package/dist/thenable.d.mts +29 -0
- package/dist/thenable.mjs +3 -3
- package/package.json +1 -1
- package/dist/all-keyed.d.mts +0 -18
- package/dist/all-keyed.mjs +0 -19
- package/dist/client.d.mts +0 -123
- package/dist/client.mjs +0 -266
- package/dist/online.mjs +0 -16
package/dist/observer.mjs
CHANGED
|
@@ -1,4 +1,43 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { Platform } from "./platform.mjs";
|
|
3
|
+
import { unsafeCast } from "./cast.mjs";
|
|
4
|
+
import { listen } from "./listen.mjs";
|
|
5
|
+
import { entryOf } from "./entry.mjs";
|
|
6
|
+
import { fetchNextPage, fetchPreviousPage, getUnsafe as getUnsafe$1, hasNextPageUnsafe, hasPreviousPageUnsafe, refetch, subscribeUnsafe as subscribeUnsafe$1 } from "./query.mjs";
|
|
7
|
+
import { Effect, Exit, FiberHandle, Pipeable, Predicate, Scope } from "effect";
|
|
1
8
|
//#region src/observer.ts
|
|
9
|
+
/**
|
|
10
|
+
* One reader's view of one query at a time, as a data type, like TanStack
|
|
11
|
+
* Query's `QueryObserver`: it derives the reader's result from the query's
|
|
12
|
+
* state, keeps a previous query's data for `whilePending: "previous"`, runs
|
|
13
|
+
* `refetchInterval`, and notifies its listeners only when a result property
|
|
14
|
+
* they read changed (see {@link trackResult}). Bindings make one per hook.
|
|
15
|
+
*
|
|
16
|
+
* The `*Unsafe` functions read and update synchronously, for renders and
|
|
17
|
+
* external stores. While it has listeners, the observer holds a `Scope` (a
|
|
18
|
+
* child of its query's) that owns the query subscription and the polling
|
|
19
|
+
* fiber; the last listener leaving, a switch to another query, or the query
|
|
20
|
+
* leaving its cache closes it.
|
|
21
|
+
*/
|
|
22
|
+
var observer_exports = /* @__PURE__ */ __exportAll({
|
|
23
|
+
TypeId: () => TypeId,
|
|
24
|
+
changes: () => changes,
|
|
25
|
+
get: () => get,
|
|
26
|
+
getOptimisticResultUnsafe: () => getOptimisticResultUnsafe,
|
|
27
|
+
getUnsafe: () => getUnsafe,
|
|
28
|
+
isQueryObserver: () => isQueryObserver,
|
|
29
|
+
make: () => make,
|
|
30
|
+
makeUnsafe: () => makeUnsafe,
|
|
31
|
+
setOptionsUnsafe: () => setOptionsUnsafe,
|
|
32
|
+
subscribeUnsafe: () => subscribeUnsafe,
|
|
33
|
+
trackProp: () => trackProp,
|
|
34
|
+
trackResult: () => trackResult
|
|
35
|
+
});
|
|
36
|
+
const TypeId = "~@raf-query/core/QueryObserver";
|
|
37
|
+
const Proto = {
|
|
38
|
+
...Pipeable.Prototype,
|
|
39
|
+
[TypeId]: { _A: (value) => value }
|
|
40
|
+
};
|
|
2
41
|
const resultKeys = {
|
|
3
42
|
error: true,
|
|
4
43
|
fetchNextPage: true,
|
|
@@ -18,150 +57,193 @@ const resultKeys = {
|
|
|
18
57
|
refetch: true,
|
|
19
58
|
updatedAt: true
|
|
20
59
|
};
|
|
60
|
+
function isQueryObserver(value) {
|
|
61
|
+
return Predicate.hasProperty(value, TypeId);
|
|
62
|
+
}
|
|
63
|
+
function implOf(self) {
|
|
64
|
+
return unsafeCast(self);
|
|
65
|
+
}
|
|
21
66
|
function isResultKey(name) {
|
|
22
|
-
return
|
|
67
|
+
return Predicate.isString(name) && Object.hasOwn(resultKeys, name);
|
|
23
68
|
}
|
|
24
69
|
function shallowEqual(a, b) {
|
|
25
70
|
for (const name of Object.keys(resultKeys)) if (isResultKey(name) && a[name] !== b[name]) return false;
|
|
26
71
|
return true;
|
|
27
72
|
}
|
|
73
|
+
/** The result for `query` with `options`, given the last query that had data. */
|
|
74
|
+
function resultOf(query, options, lastWithData) {
|
|
75
|
+
const state = getUnsafe$1(query);
|
|
76
|
+
const previous = options.whilePending === "previous" && !state.hasData && state.status !== "error" && lastWithData !== void 0 && lastWithData.hash !== query.hash ? lastWithData : void 0;
|
|
77
|
+
const shown = previous ?? query;
|
|
78
|
+
const shownState = previous ? getUnsafe$1(previous) : state;
|
|
79
|
+
return {
|
|
80
|
+
error: state.error,
|
|
81
|
+
fetchNextPage: fetchNextPage(query),
|
|
82
|
+
fetchPreviousPage: fetchPreviousPage(query),
|
|
83
|
+
hash: query.hash,
|
|
84
|
+
hasNextPage: hasNextPageUnsafe(query),
|
|
85
|
+
hasPreviousPage: hasPreviousPageUnsafe(query),
|
|
86
|
+
input: shown.handle.input,
|
|
87
|
+
isFetching: state.isFetching,
|
|
88
|
+
isFetchingNextPage: state.fetchDirection === "forward",
|
|
89
|
+
isFetchingPreviousPage: state.fetchDirection === "backward",
|
|
90
|
+
isPending: !state.hasData,
|
|
91
|
+
isPrevious: previous !== void 0,
|
|
92
|
+
isStale: state.isStale,
|
|
93
|
+
key: shown.handle.key,
|
|
94
|
+
promise: shownState.promise,
|
|
95
|
+
refetch: refetch(query),
|
|
96
|
+
updatedAt: shownState.updatedAt
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Observe `query`. Doesn't subscribe to it until the observer has a listener. */
|
|
100
|
+
function makeUnsafe(query, options = {}) {
|
|
101
|
+
return Object.assign(Object.create(Proto), {
|
|
102
|
+
current: resultOf(query, options, void 0),
|
|
103
|
+
lastWithData: getUnsafe$1(query).hasData ? query : void 0,
|
|
104
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
105
|
+
options,
|
|
106
|
+
query,
|
|
107
|
+
subscription: void 0,
|
|
108
|
+
trackedProps: /* @__PURE__ */ new Set()
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** {@link makeUnsafe}, as an Effect. */
|
|
112
|
+
function make(query, options) {
|
|
113
|
+
return Effect.sync(() => makeUnsafe(query, options));
|
|
114
|
+
}
|
|
115
|
+
/** The current result. */
|
|
116
|
+
function getUnsafe(self) {
|
|
117
|
+
return implOf(self).current;
|
|
118
|
+
}
|
|
119
|
+
/** {@link getUnsafe}, as an Effect. */
|
|
120
|
+
function get(self) {
|
|
121
|
+
return Effect.sync(() => getUnsafe(self));
|
|
122
|
+
}
|
|
28
123
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* query's data for `whilePending: "previous"`, runs `refetchInterval`, and
|
|
32
|
-
* notifies its listeners only when a result property they read changed
|
|
33
|
-
* (see {@link QueryObserver.trackResult}). Bindings create one per hook.
|
|
124
|
+
* The result for `query` with `options` right now, without switching to
|
|
125
|
+
* them. Bindings call this during render, before `setOptionsUnsafe` commits.
|
|
34
126
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
* The result for `query` with `options` right now, without switching to
|
|
52
|
-
* it. Bindings call this during render, before `setOptions` commits.
|
|
53
|
-
*/
|
|
54
|
-
getOptimisticResult(query, options) {
|
|
55
|
-
const result = this.#createResult(query, options);
|
|
56
|
-
if (!shallowEqual(this.#current, result)) this.#current = result;
|
|
57
|
-
return result;
|
|
127
|
+
function getOptimisticResultUnsafe(self, query, options) {
|
|
128
|
+
const impl = implOf(self);
|
|
129
|
+
const result = resultOf(query, options, impl.lastWithData);
|
|
130
|
+
if (!shallowEqual(impl.current, result)) impl.current = result;
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
/** Observe `query` with `options` from now on. */
|
|
134
|
+
function setOptionsUnsafe(self, query, options) {
|
|
135
|
+
const impl = implOf(self);
|
|
136
|
+
const previous = impl.query;
|
|
137
|
+
const previousInterval = impl.options.refetchInterval;
|
|
138
|
+
impl.query = query;
|
|
139
|
+
impl.options = options;
|
|
140
|
+
if (impl.listeners.size > 0) {
|
|
141
|
+
if (query !== previous) connect(impl);
|
|
142
|
+
else if (options.refetchInterval !== previousInterval) restartPolling(impl);
|
|
58
143
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.#unsubscribe?.();
|
|
69
|
-
this.#unsubscribe = this.#subscribeToQuery();
|
|
70
|
-
}
|
|
71
|
-
if (query !== previous || options.refetchInterval !== previousInterval) this.#startInterval();
|
|
72
|
-
}
|
|
73
|
-
this.updateResult();
|
|
144
|
+
updateResult(impl);
|
|
145
|
+
}
|
|
146
|
+
/** Listen for result changes. The first listener subscribes to the query. */
|
|
147
|
+
function subscribeUnsafe(self, listener) {
|
|
148
|
+
const impl = implOf(self);
|
|
149
|
+
impl.listeners.add(listener);
|
|
150
|
+
if (!impl.subscription) {
|
|
151
|
+
connect(impl);
|
|
152
|
+
updateResult(impl);
|
|
74
153
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (this.#listeners.size === 1) {
|
|
79
|
-
this.#unsubscribe = this.#subscribeToQuery();
|
|
80
|
-
this.#startInterval();
|
|
81
|
-
this.updateResult();
|
|
82
|
-
}
|
|
83
|
-
return () => {
|
|
84
|
-
if (!this.#listeners.delete(listener) || this.#listeners.size > 0) return;
|
|
85
|
-
this.#unsubscribe?.();
|
|
86
|
-
this.#unsubscribe = void 0;
|
|
87
|
-
clearInterval(this.#interval);
|
|
88
|
-
};
|
|
154
|
+
return () => {
|
|
155
|
+
if (!impl.listeners.delete(listener) || impl.listeners.size > 0) return;
|
|
156
|
+
disconnect(impl);
|
|
89
157
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.#trackedProps.add(name);
|
|
104
|
-
}
|
|
105
|
-
/** Recompute the result, and notify listeners if a tracked property changed. */
|
|
106
|
-
updateResult() {
|
|
107
|
-
const previous = this.#current;
|
|
108
|
-
const next = this.#createResult(this.#query, this.#options);
|
|
109
|
-
if (this.#query.getState().hasData) this.#lastWithData = this.#query;
|
|
110
|
-
if (shallowEqual(previous, next)) return;
|
|
111
|
-
this.#current = next;
|
|
112
|
-
if (this.#shouldNotify(previous, next)) for (const listener of this.#listeners) listener();
|
|
113
|
-
}
|
|
114
|
-
#shouldNotify(previous, next) {
|
|
115
|
-
if (this.#trackedProps.size === 0) return true;
|
|
116
|
-
for (const name of this.#trackedProps) if (previous[name] !== next[name]) return true;
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
#subscribeToQuery() {
|
|
120
|
-
const options = () => this.#options;
|
|
121
|
-
return this.#query.subscribe(() => this.updateResult(), {
|
|
122
|
-
get refetchOnReconnect() {
|
|
123
|
-
return options().refetchOnReconnect;
|
|
124
|
-
},
|
|
125
|
-
get refetchOnWindowFocus() {
|
|
126
|
-
return options().refetchOnWindowFocus;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The current result, then every change listeners are notified of. Counts
|
|
161
|
+
* as a listener while the stream runs, so the query counts as read.
|
|
162
|
+
*/
|
|
163
|
+
function changes(self) {
|
|
164
|
+
return listen((push) => {
|
|
165
|
+
let last;
|
|
166
|
+
const emit = () => {
|
|
167
|
+
const result = getUnsafe(self);
|
|
168
|
+
if (result !== last) {
|
|
169
|
+
last = result;
|
|
170
|
+
push(result);
|
|
127
171
|
}
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
#startInterval() {
|
|
131
|
-
clearInterval(this.#interval);
|
|
132
|
-
this.#interval = void 0;
|
|
133
|
-
const interval = this.#options.refetchInterval;
|
|
134
|
-
if (interval === void 0 || !Number.isFinite(interval) || interval <= 0) return;
|
|
135
|
-
this.#interval = setInterval(() => {
|
|
136
|
-
if (!("document" in globalThis) || document.visibilityState !== "hidden") this.#query.refetch();
|
|
137
|
-
}, interval);
|
|
138
|
-
}
|
|
139
|
-
#createResult(query, options) {
|
|
140
|
-
const state = query.getState();
|
|
141
|
-
const last = this.#lastWithData;
|
|
142
|
-
const previous = options.whilePending === "previous" && !state.hasData && state.status !== "error" && last !== void 0 && last.hash !== query.hash ? last : void 0;
|
|
143
|
-
const shown = previous ?? query;
|
|
144
|
-
const shownState = previous ? previous.getState() : state;
|
|
145
|
-
return {
|
|
146
|
-
error: state.error,
|
|
147
|
-
fetchNextPage: query.fetchNextPage,
|
|
148
|
-
fetchPreviousPage: query.fetchPreviousPage,
|
|
149
|
-
hash: query.hash,
|
|
150
|
-
hasNextPage: query.hasNextPage(),
|
|
151
|
-
hasPreviousPage: query.hasPreviousPage(),
|
|
152
|
-
input: shown.handle.input,
|
|
153
|
-
isFetching: state.isFetching,
|
|
154
|
-
isFetchingNextPage: state.fetchDirection === "forward",
|
|
155
|
-
isFetchingPreviousPage: state.fetchDirection === "backward",
|
|
156
|
-
isPending: !state.hasData,
|
|
157
|
-
isPrevious: previous !== void 0,
|
|
158
|
-
isStale: state.isStale,
|
|
159
|
-
key: shown.handle.key,
|
|
160
|
-
promise: shownState.promise,
|
|
161
|
-
refetch: query.refetch,
|
|
162
|
-
updatedAt: shownState.updatedAt
|
|
163
172
|
};
|
|
164
|
-
|
|
165
|
-
|
|
173
|
+
const unsubscribe = subscribeUnsafe(self, emit);
|
|
174
|
+
emit();
|
|
175
|
+
return unsubscribe;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Wrap `result` so that reading a property marks it tracked. Once any
|
|
180
|
+
* property is tracked, listeners are only notified when a tracked one
|
|
181
|
+
* changes.
|
|
182
|
+
*/
|
|
183
|
+
function trackResult(self, result) {
|
|
184
|
+
return new Proxy(result, { get: (target, name, receiver) => {
|
|
185
|
+
if (isResultKey(name)) trackProp(self, name);
|
|
186
|
+
return Reflect.get(target, name, receiver);
|
|
187
|
+
} });
|
|
188
|
+
}
|
|
189
|
+
/** Mark `name` as read, for bindings that track access themselves. */
|
|
190
|
+
function trackProp(self, name) {
|
|
191
|
+
implOf(self).trackedProps.add(name);
|
|
192
|
+
}
|
|
193
|
+
/** Recompute the result, and notify listeners if a tracked property changed. */
|
|
194
|
+
function updateResult(self) {
|
|
195
|
+
const previous = self.current;
|
|
196
|
+
const next = resultOf(self.query, self.options, self.lastWithData);
|
|
197
|
+
if (getUnsafe$1(self.query).hasData) self.lastWithData = self.query;
|
|
198
|
+
if (shallowEqual(previous, next)) return;
|
|
199
|
+
self.current = next;
|
|
200
|
+
if (shouldNotify(self, previous, next)) for (const listener of self.listeners) listener();
|
|
201
|
+
}
|
|
202
|
+
function shouldNotify(self, previous, next) {
|
|
203
|
+
if (self.trackedProps.size === 0) return true;
|
|
204
|
+
for (const name of self.trackedProps) if (previous[name] !== next[name]) return true;
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Subscribe to the current query in a new scope under its entry's, and
|
|
209
|
+
* start polling. Replaces any previous subscription.
|
|
210
|
+
*/
|
|
211
|
+
function connect(self) {
|
|
212
|
+
disconnect(self);
|
|
213
|
+
const query = self.query;
|
|
214
|
+
const preferences = {
|
|
215
|
+
get refetchOnReconnect() {
|
|
216
|
+
return self.options.refetchOnReconnect;
|
|
217
|
+
},
|
|
218
|
+
get refetchOnWindowFocus() {
|
|
219
|
+
return self.options.refetchOnWindowFocus;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const scope = Scope.forkUnsafe(entryOf(query).scope);
|
|
223
|
+
self.subscription = {
|
|
224
|
+
polls: Effect.runSync(FiberHandle.make().pipe(Effect.tap(Effect.acquireRelease(Effect.sync(() => subscribeUnsafe$1(query, () => updateResult(self), preferences)), (unsubscribe) => Effect.sync(unsubscribe))), Scope.provide(scope))),
|
|
225
|
+
scope
|
|
226
|
+
};
|
|
227
|
+
restartPolling(self);
|
|
228
|
+
}
|
|
229
|
+
function disconnect(self) {
|
|
230
|
+
const subscription = self.subscription;
|
|
231
|
+
self.subscription = void 0;
|
|
232
|
+
if (subscription) Effect.runFork(Scope.close(subscription.scope, Exit.void));
|
|
233
|
+
}
|
|
234
|
+
/** Run `poll` for the current `refetchInterval`, or stop polling without one. */
|
|
235
|
+
function restartPolling(self) {
|
|
236
|
+
const subscription = self.subscription;
|
|
237
|
+
if (!subscription) return;
|
|
238
|
+
const interval = self.options.refetchInterval;
|
|
239
|
+
const polls = subscription.polls;
|
|
240
|
+
Effect.runForkWith(entryOf(self.query).cache.services)(interval !== void 0 && Number.isFinite(interval) && interval > 0 ? FiberHandle.run(polls, poll(self.query, interval)) : FiberHandle.clear(polls));
|
|
241
|
+
}
|
|
242
|
+
/** Refetch `query` every `interval` ms while the page is visible. */
|
|
243
|
+
function poll(query, interval) {
|
|
244
|
+
return Effect.gen(function* () {
|
|
245
|
+
if ((yield* Platform).isVisibleUnsafe()) yield* Effect.forkChild(refetch(query), { startImmediately: true });
|
|
246
|
+
}).pipe(Effect.delay(interval), Effect.forever);
|
|
247
|
+
}
|
|
166
248
|
//#endregion
|
|
167
|
-
export {
|
|
249
|
+
export { TypeId, changes, get, getOptimisticResultUnsafe, getUnsafe, isQueryObserver, make, makeUnsafe, observer_exports, setOptionsUnsafe, subscribeUnsafe, trackProp, trackResult };
|
package/dist/options.d.mts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { QueryKey } from "./key.mjs";
|
|
2
2
|
import { Effect, Schedule } from "effect";
|
|
3
3
|
//#region src/options.d.ts
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* What a `queryFn` may return: a value, a promise, or an `Effect`. An
|
|
6
|
+
* Effect's error type becomes the query's (promises reject with `unknown`),
|
|
7
|
+
* and the services it needs become requirements of the verbs that start it.
|
|
8
|
+
*/
|
|
9
|
+
export type QueryFnResult<TData, TError = unknown, TServices = never> = TData | PromiseLike<TData> | Effect.Effect<TData, TError, TServices>;
|
|
6
10
|
export interface QueryFunctionContext {
|
|
7
11
|
/** The query's key, as returned by the factory. */
|
|
8
12
|
readonly queryKey: QueryKey;
|
|
@@ -20,7 +24,7 @@ export interface PageFunctionContext<TPageParam> extends QueryFunctionContext {
|
|
|
20
24
|
* of failures so far (1 after the first).
|
|
21
25
|
*/
|
|
22
26
|
export type RetryPolicy = boolean | number | Schedule.Schedule<unknown, unknown> | ((failureCount: number, error: unknown) => boolean);
|
|
23
|
-
/** Options every query accepts. `
|
|
27
|
+
/** Options every query accepts. `QueryCache` defaults fill in the rest. */
|
|
24
28
|
export interface QueryBehavior {
|
|
25
29
|
/** How long an unread entry stays cached, in ms. `Infinity` keeps it. */
|
|
26
30
|
readonly gcTime?: number;
|
|
@@ -41,12 +45,21 @@ export interface QueryBehavior {
|
|
|
41
45
|
*/
|
|
42
46
|
readonly structuralSharing?: boolean;
|
|
43
47
|
}
|
|
44
|
-
|
|
48
|
+
declare const servicesType: unique symbol;
|
|
49
|
+
interface ServicesField<TServices> {
|
|
50
|
+
/**
|
|
51
|
+
* Type-only: the services the `queryFn`'s Effect needs. Never present at
|
|
52
|
+
* runtime. A `queryFn`'s return type can't carry them on its own: with
|
|
53
|
+
* `unknown` data, any return type is assignable to `QueryFnResult`.
|
|
54
|
+
*/
|
|
55
|
+
readonly [servicesType]?: () => TServices;
|
|
56
|
+
}
|
|
57
|
+
export interface QueryOptions<TData, TError = unknown, TServices = never> extends QueryBehavior, ServicesField<TServices> {
|
|
45
58
|
readonly kind: "query";
|
|
46
|
-
queryFn(context: QueryFunctionContext): QueryFnResult<TData>;
|
|
59
|
+
queryFn(context: QueryFunctionContext): QueryFnResult<TData, TError, TServices>;
|
|
47
60
|
readonly queryKey: QueryKey;
|
|
48
61
|
}
|
|
49
|
-
export interface InfiniteQueryOptions<TPage, TPageParam> extends QueryBehavior {
|
|
62
|
+
export interface InfiniteQueryOptions<TPage, TPageParam, TError = unknown, TServices = never> extends QueryBehavior, ServicesField<TServices> {
|
|
50
63
|
/** Param for the page after `lastPage`, or `null` / `undefined` at the end. */
|
|
51
64
|
getNextPageParam(lastPage: TPage, pages: ReadonlyArray<TPage>, lastPageParam: TPageParam, pageParams: ReadonlyArray<TPageParam>): TPageParam | null | undefined;
|
|
52
65
|
/** Param for the page before `firstPage`, or `null` / `undefined` at the start. */
|
|
@@ -55,7 +68,7 @@ export interface InfiniteQueryOptions<TPage, TPageParam> extends QueryBehavior {
|
|
|
55
68
|
readonly kind: "infinite";
|
|
56
69
|
/** Keep at most this many pages; the far end is dropped when paging. */
|
|
57
70
|
readonly maxPages?: number;
|
|
58
|
-
queryFn(context: PageFunctionContext<TPageParam>): QueryFnResult<TPage>;
|
|
71
|
+
queryFn(context: PageFunctionContext<TPageParam>): QueryFnResult<TPage, TError, TServices>;
|
|
59
72
|
readonly queryKey: QueryKey;
|
|
60
73
|
}
|
|
61
74
|
/** Data of a paged query. */
|
|
@@ -63,7 +76,7 @@ export interface InfiniteData<TPage, TPageParam = unknown> {
|
|
|
63
76
|
readonly pageParams: ReadonlyArray<TPageParam>;
|
|
64
77
|
readonly pages: ReadonlyArray<TPage>;
|
|
65
78
|
}
|
|
66
|
-
export type AnyQueryOptions = QueryOptions<unknown> | InfiniteQueryOptions<unknown, unknown>;
|
|
79
|
+
export type AnyQueryOptions = QueryOptions<unknown, unknown, unknown> | InfiniteQueryOptions<unknown, unknown, unknown, unknown>;
|
|
67
80
|
/**
|
|
68
81
|
* Typed options for a query factory:
|
|
69
82
|
*
|
|
@@ -75,16 +88,28 @@ export type AnyQueryOptions = QueryOptions<unknown> | InfiniteQueryOptions<unkno
|
|
|
75
88
|
* });
|
|
76
89
|
* }
|
|
77
90
|
* ```
|
|
91
|
+
*
|
|
92
|
+
* A `queryFn` Effect may need services; they're inferred, and every verb
|
|
93
|
+
* that can start the query requires them.
|
|
78
94
|
*/
|
|
79
|
-
export declare function queryOptions<TData>(options: Omit<QueryOptions<TData>, "kind">): QueryOptions<TData>;
|
|
95
|
+
export declare function queryOptions<TData, TError = unknown, TServices = never>(options: Omit<QueryOptions<TData, TError, TServices>, "kind">): QueryOptions<TData, TError, TServices>;
|
|
80
96
|
/** Typed options for a paged query factory. */
|
|
81
|
-
export declare function infiniteQueryOptions<TPage, TPageParam>(options: Omit<InfiniteQueryOptions<TPage, TPageParam>, "kind">): InfiniteQueryOptions<TPage, TPageParam>;
|
|
97
|
+
export declare function infiniteQueryOptions<TPage, TPageParam, TError = unknown, TServices = never>(options: Omit<InfiniteQueryOptions<TPage, TPageParam, TError, TServices>, "kind">): InfiniteQueryOptions<TPage, TPageParam, TError, TServices>;
|
|
98
|
+
interface FactoryResult {
|
|
99
|
+
readonly kind: "infinite" | "query";
|
|
100
|
+
readonly queryKey: QueryKey;
|
|
101
|
+
}
|
|
82
102
|
/**
|
|
83
103
|
* A function of zero or one argument returning `queryOptions(...)` or
|
|
84
|
-
* `infiniteQueryOptions(...)
|
|
104
|
+
* `infiniteQueryOptions(...)` whose `queryFn` needs at most `TServices`
|
|
105
|
+
* (any services by default). A second parameter is a type error: the single
|
|
85
106
|
* argument is the query's input, and a handle carries it.
|
|
107
|
+
*
|
|
108
|
+
* The return type names no `queryFn`, and no services for the default:
|
|
109
|
+
* TypeScript infers an inline factory's `queryOptions` type arguments from
|
|
110
|
+
* it, and an `unknown` there would infer `unknown` services.
|
|
86
111
|
*/
|
|
87
|
-
export type QueryFactory = (input: never) =>
|
|
112
|
+
export type QueryFactory<TServices = unknown> = (input: never) => unknown extends TServices ? FactoryResult : FactoryResult & ServicesField<TServices>;
|
|
88
113
|
/** The factory's input type; `undefined` for factories without one. */
|
|
89
114
|
export type InputOf<TFactory extends QueryFactory> = Parameters<TFactory> extends [] ? undefined : Parameters<TFactory>[0];
|
|
90
115
|
/** Arguments after the factory: the input, optional when the factory's is. */
|
|
@@ -94,9 +119,20 @@ export type IsPaged<TFactory extends QueryFactory> = ReturnType<TFactory> extend
|
|
|
94
119
|
readonly kind: "infinite";
|
|
95
120
|
} ? true : false;
|
|
96
121
|
/** The data a factory's query resolves to. */
|
|
97
|
-
export type DataOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TPage, infer TPageParam> ? InfiniteData<TPage, TPageParam> : ReturnType<TFactory> extends QueryOptions<infer TData> ? TData : never;
|
|
122
|
+
export type DataOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TPage, infer TPageParam, infer _TError, infer _TServices> ? InfiniteData<TPage, TPageParam> : ReturnType<TFactory> extends QueryOptions<infer TData, infer _TError, infer _TServices> ? TData : never;
|
|
123
|
+
/**
|
|
124
|
+
* The error a factory's request fails with: the `queryFn`'s Effect error
|
|
125
|
+
* type, or `unknown` for promises and plain values.
|
|
126
|
+
*/
|
|
127
|
+
export type ErrorOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer _TPage, infer _TPageParam, infer TError, infer _TServices> ? TError : ReturnType<TFactory> extends QueryOptions<infer _TData, infer TError, infer _TServices> ? TError : unknown;
|
|
128
|
+
/**
|
|
129
|
+
* The services a factory's `queryFn` Effect needs: what `ensure`, `fetch`,
|
|
130
|
+
* `prefetch`, `getQuery` and `watch` require. `never` for promises and plain
|
|
131
|
+
* values, and for a factory of no particular query.
|
|
132
|
+
*/
|
|
133
|
+
export type ServicesOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer _TPage, infer _TPageParam, infer _TError, infer TServices> ? TServices : ReturnType<TFactory> extends QueryOptions<infer _TData, infer _TError, infer TServices> ? TServices : never;
|
|
98
134
|
/** Page type of a paged factory. */
|
|
99
|
-
export type PageOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TPage, unknown> ? TPage : never;
|
|
135
|
+
export type PageOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TPage, unknown, infer _TError, infer _TServices> ? TPage : never;
|
|
100
136
|
/** `ensure` / `fetch` options. Only paged factories accept `pages`. */
|
|
101
137
|
export type LoadOptions<TFactory extends QueryFactory> = IsPaged<TFactory> extends true ? {
|
|
102
138
|
/** Load this many pages up front (default 1). */
|
package/dist/options.mjs
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Context, Effect } from "effect";
|
|
2
|
+
declare namespace platform_d_exports {
|
|
3
|
+
export { Platform, PlatformEvent, whenOnline };
|
|
4
|
+
}
|
|
5
|
+
/** `"focus"`: the page became visible. `"online"`: the browser reconnected. */
|
|
6
|
+
export type PlatformEvent = "focus" | "online";
|
|
7
|
+
/**
|
|
8
|
+
* The page a cache runs in: whether there is one, and its focus and network
|
|
9
|
+
* state. The default reads the global `window`, `document` and `navigator`;
|
|
10
|
+
* provide another to run a cache against a fake page.
|
|
11
|
+
*/
|
|
12
|
+
export interface Platform {
|
|
13
|
+
isOnlineUnsafe(): boolean;
|
|
14
|
+
/** No page to watch: a server render. */
|
|
15
|
+
readonly isServer: boolean;
|
|
16
|
+
isVisibleUnsafe(): boolean;
|
|
17
|
+
/** Call `listener` on every focus and reconnect until unsubscribed. */
|
|
18
|
+
subscribeUnsafe(listener: (event: PlatformEvent) => void): () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare const Platform: Context.Reference<Platform>;
|
|
21
|
+
/**
|
|
22
|
+
* Succeeds now if the platform is online, else on its next `"online"` event.
|
|
23
|
+
* Interrupting it unsubscribes.
|
|
24
|
+
*/
|
|
25
|
+
export declare const whenOnline: Effect.Effect<void>;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { platform_d_exports };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { Context, Effect } from "effect";
|
|
3
|
+
//#region src/platform.ts
|
|
4
|
+
var platform_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
Platform: () => Platform,
|
|
6
|
+
whenOnline: () => whenOnline
|
|
7
|
+
});
|
|
8
|
+
const globalPlatform = {
|
|
9
|
+
isOnlineUnsafe: () => !("window" in globalThis) || !("navigator" in globalThis) || !("onLine" in navigator) || navigator.onLine,
|
|
10
|
+
get isServer() {
|
|
11
|
+
return !("window" in globalThis);
|
|
12
|
+
},
|
|
13
|
+
isVisibleUnsafe: () => !("document" in globalThis) || document.visibilityState !== "hidden",
|
|
14
|
+
subscribeUnsafe(listener) {
|
|
15
|
+
if (!("window" in globalThis)) return () => {};
|
|
16
|
+
const onVisibilityChange = () => {
|
|
17
|
+
if (document.visibilityState === "visible") listener("focus");
|
|
18
|
+
};
|
|
19
|
+
const onOnline = () => listener("online");
|
|
20
|
+
window.addEventListener("visibilitychange", onVisibilityChange);
|
|
21
|
+
window.addEventListener("online", onOnline);
|
|
22
|
+
return () => {
|
|
23
|
+
window.removeEventListener("visibilitychange", onVisibilityChange);
|
|
24
|
+
window.removeEventListener("online", onOnline);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const Platform = Context.Reference("@raf-query/core/Platform", { defaultValue: () => globalPlatform });
|
|
29
|
+
/**
|
|
30
|
+
* Succeeds now if the platform is online, else on its next `"online"` event.
|
|
31
|
+
* Interrupting it unsubscribes.
|
|
32
|
+
*/
|
|
33
|
+
const whenOnline = Effect.gen(function* () {
|
|
34
|
+
const platform = yield* Platform;
|
|
35
|
+
if (platform.isOnlineUnsafe()) return;
|
|
36
|
+
yield* Effect.callback((resume) => {
|
|
37
|
+
const unsubscribe = platform.subscribeUnsafe((event) => {
|
|
38
|
+
if (event === "online") resume(Effect.void);
|
|
39
|
+
});
|
|
40
|
+
return Effect.sync(unsubscribe);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//#endregion
|
|
44
|
+
export { Platform, platform_exports, whenOnline };
|