@raf-query/core 0.11.1 → 0.13.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 +39 -1
- package/README.md +28 -18
- package/dist/client.d.mts +163 -119
- package/dist/client.mjs +90 -162
- package/dist/entry.d.mts +73 -147
- package/dist/entry.mjs +314 -471
- package/dist/features/focus-refetch/focus-refetch.d.mts +17 -0
- package/dist/features/focus-refetch/focus-refetch.mjs +20 -0
- package/dist/features/polling/polling.d.mts +16 -0
- package/dist/features/polling/polling.mjs +39 -0
- package/dist/features/reconnect-refetch/reconnect-refetch.d.mts +17 -0
- package/dist/features/reconnect-refetch/reconnect-refetch.mjs +16 -0
- package/dist/features/shared.mjs +13 -0
- package/dist/guards.mjs +23 -0
- package/dist/hydration/hydration.d.mts +108 -0
- package/dist/hydration/hydration.mjs +142 -0
- package/dist/index.d.mts +19 -13
- package/dist/index.mjs +16 -13
- package/dist/infinite/infinite.d.mts +83 -0
- package/dist/infinite/infinite.mjs +120 -0
- package/dist/infinite/paging.d.mts +71 -0
- package/dist/infinite/paging.mjs +62 -0
- package/dist/internal.d.mts +13 -0
- package/dist/internal.mjs +7 -0
- package/dist/keys/keys.d.mts +54 -0
- package/dist/keys/keys.mjs +53 -0
- package/dist/keys/targets.d.mts +62 -0
- package/dist/keys/targets.mjs +85 -0
- package/dist/links/retry/retry.d.mts +33 -0
- package/dist/links/retry/retry.mjs +53 -0
- package/dist/links/timeout/timeout.d.mts +17 -0
- package/dist/links/timeout/timeout.mjs +35 -0
- package/dist/load.d.mts +149 -0
- package/dist/load.mjs +179 -0
- package/dist/options.d.mts +161 -332
- package/dist/options.mjs +28 -40
- package/dist/presets.d.mts +35 -0
- package/dist/presets.mjs +27 -0
- package/dist/root.d.mts +86 -0
- package/dist/structural-sharing/structural-sharing.mjs +32 -0
- package/dist/subscribe.d.mts +15 -0
- package/dist/subscribe.mjs +29 -0
- package/dist/thenable.d.mts +15 -17
- package/dist/thenable.mjs +25 -6
- package/dist/verbs.d.mts +150 -0
- package/dist/verbs.mjs +115 -0
- package/package.json +13 -9
- package/dist/_virtual/_rolldown/runtime.mjs +0 -13
- package/dist/all-keyed.d.mts +0 -18
- package/dist/all-keyed.mjs +0 -19
- package/dist/binding.d.mts +0 -92
- package/dist/binding.mjs +0 -186
- package/dist/cache.d.mts +0 -164
- package/dist/cache.mjs +0 -184
- package/dist/controls.d.mts +0 -95
- package/dist/controls.mjs +0 -70
- package/dist/hydration.d.mts +0 -84
- package/dist/hydration.mjs +0 -148
- package/dist/infinite.d.mts +0 -5
- package/dist/infinite.mjs +0 -103
- package/dist/key.d.mts +0 -18
- package/dist/key.mjs +0 -36
- package/dist/listen.mjs +0 -19
- package/dist/notify.mjs +0 -21
- package/dist/observer.d.mts +0 -110
- package/dist/observer.mjs +0 -300
- package/dist/platform.d.mts +0 -27
- package/dist/platform.mjs +0 -44
- package/dist/query.d.mts +0 -137
- package/dist/query.mjs +0 -147
- package/dist/request.mjs +0 -47
- package/dist/retry.mjs +0 -25
- package/dist/share.mjs +0 -40
package/dist/all-keyed.mjs
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
//#region src/all-keyed.ts
|
|
2
|
-
/**
|
|
3
|
-
* Ponyfill of the TC39 `Promise.allKeyed` proposal: a record of promises in,
|
|
4
|
-
* a promise of the same record out, rejecting on the first failure.
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* const data = await allKeyed({
|
|
8
|
-
* post: qc.ensure(postQuery, { slug }),
|
|
9
|
-
* author: qc.ensure(authorQuery, { slug }),
|
|
10
|
-
* });
|
|
11
|
-
* ```
|
|
12
|
-
*/
|
|
13
|
-
async function allKeyed(record) {
|
|
14
|
-
const keys = Object.keys(record);
|
|
15
|
-
const values = await Promise.all(keys.map((key) => record[key]));
|
|
16
|
-
return Object.fromEntries(keys.map((key, index) => [key, values[index]]));
|
|
17
|
-
}
|
|
18
|
-
//#endregion
|
|
19
|
-
export { allKeyed };
|
package/dist/binding.d.mts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { QueryFactory, QueryHandle } from "./options.mjs";
|
|
2
|
-
import { Query, query_d_exports } from "./query.mjs";
|
|
3
|
-
import { QueryClient } from "./client.mjs";
|
|
4
|
-
import { Options, QueryObserver, observer_d_exports } from "./observer.mjs";
|
|
5
|
-
import { ReadValue, UseQueryControls, UseQueryOptions, UseQueryThrowOptions } from "./controls.mjs";
|
|
6
|
-
declare namespace binding_d_exports {
|
|
7
|
-
export { Hooks, QueryBinding, ReadOptions, Result, ResultWithData, Source, makeUnsafe, observerOptions, queryFor, subscribeUnsafe, syncUnsafe };
|
|
8
|
-
}
|
|
9
|
-
/** A read's options: the observer's, and `throwOnError` for typed errors. */
|
|
10
|
-
export type ReadOptions<TFactory extends QueryFactory, TThrow extends boolean | undefined = undefined> = UseQueryOptions & UseQueryThrowOptions<TFactory> & {
|
|
11
|
-
readonly throwOnError?: TThrow;
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* What a binding's `useQuery` returns: the query's controls, the `promise`
|
|
15
|
-
* of the data to show, and that `data` once it's there. Each property is a
|
|
16
|
-
* getter that calls {@link Hooks.track}, so destructuring reads it once.
|
|
17
|
-
*/
|
|
18
|
-
export type Result<TFactory extends QueryFactory, TThrow extends boolean | undefined = undefined> = UseQueryControls<TFactory> & {
|
|
19
|
-
/** The promise's data once it has settled, else `undefined`. */
|
|
20
|
-
readonly data: ReadValue<TFactory, TThrow> | undefined;
|
|
21
|
-
/**
|
|
22
|
-
* Settles with the data to show. A new promise per data version, already
|
|
23
|
-
* settled once the cache has data.
|
|
24
|
-
*/
|
|
25
|
-
readonly promise: Promise<ReadValue<TFactory, TThrow>>;
|
|
26
|
-
};
|
|
27
|
-
/** {@link Result} for a handle from `ensure` or `fetch`. */
|
|
28
|
-
export type ResultWithData<TFactory extends QueryFactory, TThrow extends boolean | undefined = undefined> = Result<TFactory, TThrow> & {
|
|
29
|
-
/** The query's data. The handle's `data` until the cache has data again. */
|
|
30
|
-
readonly data: ReadValue<TFactory, TThrow>;
|
|
31
|
-
};
|
|
32
|
-
/** How a framework plugs into a binding. */
|
|
33
|
-
export interface Hooks {
|
|
34
|
-
/**
|
|
35
|
-
* Runs when the result changed: a property something read, or a pending
|
|
36
|
-
* promise something read settling in place. Typically bumps the version.
|
|
37
|
-
*/
|
|
38
|
-
readonly notify: () => void;
|
|
39
|
-
/**
|
|
40
|
-
* Runs on every read of the result, before the read, so the framework
|
|
41
|
-
* re-runs whatever read it after {@link Hooks.notify}. Typically reads a
|
|
42
|
-
* version signal.
|
|
43
|
-
*/
|
|
44
|
-
readonly track: () => void;
|
|
45
|
-
/**
|
|
46
|
-
* Runs cache writes (starting a read can notify observers synchronously)
|
|
47
|
-
* outside the framework's tracking. Default: runs `f` as is.
|
|
48
|
-
*/
|
|
49
|
-
readonly untracked?: <A>(f: () => A) => A;
|
|
50
|
-
}
|
|
51
|
-
/** Where a binding reads its handle and options from. */
|
|
52
|
-
export interface Source<TFactory extends QueryFactory> {
|
|
53
|
-
readonly handle: () => QueryHandle<TFactory>;
|
|
54
|
-
readonly options: () => UseQueryOptions & {
|
|
55
|
-
readonly throwOnError?: boolean;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/** One component's read of a query. */
|
|
59
|
-
export interface QueryBinding<TFactory extends QueryFactory = QueryFactory> {
|
|
60
|
-
readonly client: QueryClient<unknown>;
|
|
61
|
-
readonly observer: QueryObserver<TFactory>;
|
|
62
|
-
/** The result, the same object for the binding's life. */
|
|
63
|
-
readonly result: Result<TFactory, boolean>;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* The query a handle points at. Calls the factory, so callers keep it for
|
|
67
|
-
* as long as the handle is the same.
|
|
68
|
-
*/
|
|
69
|
-
export declare function queryFor<TFactory extends QueryFactory>(client: QueryClient<unknown>, factory: TFactory, handle: QueryHandle<TFactory>): Query<TFactory>;
|
|
70
|
-
/** The observer's part of a read's options. */
|
|
71
|
-
export declare function observerOptions(options: UseQueryOptions): Options;
|
|
72
|
-
/**
|
|
73
|
-
* Bind `factory` to the handle and options `source` returns. Reads the
|
|
74
|
-
* query and observes it, but doesn't subscribe: call {@link subscribeUnsafe}
|
|
75
|
-
* where the framework mounts, in the browser.
|
|
76
|
-
*/
|
|
77
|
-
export declare function makeUnsafe<TFactory extends QueryFactory>(client: QueryClient<unknown>, factory: TFactory, source: Source<TFactory>, hooks: Hooks): QueryBinding<TFactory>;
|
|
78
|
-
/**
|
|
79
|
-
* Follow the source: switch to a new handle's query, or new options. Reads
|
|
80
|
-
* call it themselves, so markup that reads before the framework's effects
|
|
81
|
-
* run still sees the new query. Bindings also call it from an effect, so a
|
|
82
|
-
* new handle is observed even when nothing reads the result.
|
|
83
|
-
*/
|
|
84
|
-
export declare function syncUnsafe<TFactory extends QueryFactory>(self: QueryBinding<TFactory>): void;
|
|
85
|
-
/**
|
|
86
|
-
* Notify {@link Hooks.notify} on result changes from now on. A server
|
|
87
|
-
* render reads once and never unmounts, so bindings call it in the browser
|
|
88
|
-
* only. Returns the unsubscribe.
|
|
89
|
-
*/
|
|
90
|
-
export declare function subscribeUnsafe<TFactory extends QueryFactory>(self: QueryBinding<TFactory>): () => void;
|
|
91
|
-
//#endregion
|
|
92
|
-
export { binding_d_exports };
|
package/dist/binding.mjs
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
-
import { hashKey } from "./key.mjs";
|
|
3
|
-
import { readUnsafe } from "./query.mjs";
|
|
4
|
-
import { promiseControls, resultThenable, throwsOnError } from "./controls.mjs";
|
|
5
|
-
import { getOptimisticResultUnsafe, getUnsafe, makeUnsafe as makeUnsafe$1, setOptionsUnsafe, subscribeUnsafe as subscribeUnsafe$1, trackProp } from "./observer.mjs";
|
|
6
|
-
import { Result } from "effect";
|
|
7
|
-
//#region src/binding.ts
|
|
8
|
-
/**
|
|
9
|
-
* What a framework's `useQuery` does, without the framework: one observer
|
|
10
|
-
* per component, following the handle and options it reads through
|
|
11
|
-
* getters, and a result of getters over the observer. A binding supplies
|
|
12
|
-
* {@link Hooks}: how a read is tracked, how a change is announced, and how
|
|
13
|
-
* cache writes run outside the framework's tracking. The Vue and Svelte
|
|
14
|
-
* packages are this plus their reactivity and lifecycle.
|
|
15
|
-
*
|
|
16
|
-
* Synchronous throughout, for renders.
|
|
17
|
-
*/
|
|
18
|
-
var binding_exports = /* @__PURE__ */ __exportAll({
|
|
19
|
-
makeUnsafe: () => makeUnsafe,
|
|
20
|
-
observerOptions: () => observerOptions,
|
|
21
|
-
queryFor: () => queryFor,
|
|
22
|
-
subscribeUnsafe: () => subscribeUnsafe,
|
|
23
|
-
syncUnsafe: () => syncUnsafe
|
|
24
|
-
});
|
|
25
|
-
/**
|
|
26
|
-
* The query a handle points at. Calls the factory, so callers keep it for
|
|
27
|
-
* as long as the handle is the same.
|
|
28
|
-
*/
|
|
29
|
-
function queryFor(client, factory, handle) {
|
|
30
|
-
const args = [handle.input];
|
|
31
|
-
return client.getQuery(factory, ...args);
|
|
32
|
-
}
|
|
33
|
-
/** The observer's part of a read's options. */
|
|
34
|
-
function observerOptions(options) {
|
|
35
|
-
return {
|
|
36
|
-
refetchInterval: options.refetchInterval,
|
|
37
|
-
refetchOnReconnect: options.refetchOnReconnect,
|
|
38
|
-
refetchOnWindowFocus: options.refetchOnWindowFocus,
|
|
39
|
-
whilePending: options.whilePending
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function sameObserverOptions(a, b) {
|
|
43
|
-
return a.refetchInterval === b.refetchInterval && a.refetchOnReconnect === b.refetchOnReconnect && a.refetchOnWindowFocus === b.refetchOnWindowFocus && a.whilePending === b.whilePending;
|
|
44
|
-
}
|
|
45
|
-
function stateOf(self) {
|
|
46
|
-
return self;
|
|
47
|
-
}
|
|
48
|
-
const runNow = (f) => f();
|
|
49
|
-
/**
|
|
50
|
-
* Bind `factory` to the handle and options `source` returns. Reads the
|
|
51
|
-
* query and observes it, but doesn't subscribe: call {@link subscribeUnsafe}
|
|
52
|
-
* where the framework mounts, in the browser.
|
|
53
|
-
*/
|
|
54
|
-
function makeUnsafe(client, factory, source, hooks) {
|
|
55
|
-
return (hooks.untracked ?? runNow)(() => {
|
|
56
|
-
const handle = source.handle();
|
|
57
|
-
const query = queryFor(client, factory, handle);
|
|
58
|
-
const options = observerOptions(source.options());
|
|
59
|
-
readUnsafe(query);
|
|
60
|
-
const state = {
|
|
61
|
-
awaited: /* @__PURE__ */ new WeakSet(),
|
|
62
|
-
client,
|
|
63
|
-
factory,
|
|
64
|
-
handle,
|
|
65
|
-
hooks,
|
|
66
|
-
observer: makeUnsafe$1(query, options),
|
|
67
|
-
options,
|
|
68
|
-
query,
|
|
69
|
-
source
|
|
70
|
-
};
|
|
71
|
-
return Object.assign(state, { result: resultOf(state) });
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Follow the source: switch to a new handle's query, or new options. Reads
|
|
76
|
-
* call it themselves, so markup that reads before the framework's effects
|
|
77
|
-
* run still sees the new query. Bindings also call it from an effect, so a
|
|
78
|
-
* new handle is observed even when nothing reads the result.
|
|
79
|
-
*/
|
|
80
|
-
function syncUnsafe(self) {
|
|
81
|
-
sync(stateOf(self));
|
|
82
|
-
}
|
|
83
|
-
function sync(impl) {
|
|
84
|
-
const handle = impl.source.handle();
|
|
85
|
-
const options = observerOptions(impl.source.options());
|
|
86
|
-
const newQuery = handle !== impl.handle && hashKey(handle.key) !== impl.query.hash;
|
|
87
|
-
impl.handle = handle;
|
|
88
|
-
if (!newQuery && sameObserverOptions(options, impl.options)) return;
|
|
89
|
-
(impl.hooks.untracked ?? runNow)(() => {
|
|
90
|
-
if (newQuery) {
|
|
91
|
-
impl.query = queryFor(impl.client, impl.factory, handle);
|
|
92
|
-
readUnsafe(impl.query);
|
|
93
|
-
}
|
|
94
|
-
impl.options = options;
|
|
95
|
-
setOptionsUnsafe(impl.observer, impl.query, options);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Notify {@link Hooks.notify} on result changes from now on. A server
|
|
100
|
-
* render reads once and never unmounts, so bindings call it in the browser
|
|
101
|
-
* only. Returns the unsubscribe.
|
|
102
|
-
*/
|
|
103
|
-
function subscribeUnsafe(self) {
|
|
104
|
-
const impl = stateOf(self);
|
|
105
|
-
return subscribeUnsafe$1(impl.observer, impl.hooks.notify);
|
|
106
|
-
}
|
|
107
|
-
function resultOf(impl) {
|
|
108
|
-
const { hooks, observer } = impl;
|
|
109
|
-
function read(name) {
|
|
110
|
-
sync(impl);
|
|
111
|
-
hooks.track();
|
|
112
|
-
trackProp(observer, name);
|
|
113
|
-
return impl.client.isServer ? getOptimisticResultUnsafe(observer, impl.query, impl.options) : getUnsafe(observer);
|
|
114
|
-
}
|
|
115
|
-
function throwOnError() {
|
|
116
|
-
return throwsOnError(impl.query, impl.source.options().throwOnError, impl.client.throwOnError);
|
|
117
|
-
}
|
|
118
|
-
function shown() {
|
|
119
|
-
const promise = read("promise").promise;
|
|
120
|
-
if (promise.status === "pending" && !impl.awaited.has(promise)) {
|
|
121
|
-
impl.awaited.add(promise);
|
|
122
|
-
promise.then(hooks.notify, hooks.notify);
|
|
123
|
-
}
|
|
124
|
-
return throwOnError() ? promise : resultThenable(promise);
|
|
125
|
-
}
|
|
126
|
-
function controls() {
|
|
127
|
-
return promiseControls(getUnsafe(observer));
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
get data() {
|
|
131
|
-
const promise = shown();
|
|
132
|
-
if (promise.status === "fulfilled") return promise.value;
|
|
133
|
-
const handle = impl.handle;
|
|
134
|
-
if (!("data" in handle)) return;
|
|
135
|
-
const data = handle.data;
|
|
136
|
-
return throwOnError() ? data : Result.succeed(data);
|
|
137
|
-
},
|
|
138
|
-
get error() {
|
|
139
|
-
return read("error").error;
|
|
140
|
-
},
|
|
141
|
-
fetchNextPage: () => controls().fetchNextPage(),
|
|
142
|
-
fetchPreviousPage: () => controls().fetchPreviousPage(),
|
|
143
|
-
get hash() {
|
|
144
|
-
return read("hash").hash;
|
|
145
|
-
},
|
|
146
|
-
get hasNextPage() {
|
|
147
|
-
return read("hasNextPage").hasNextPage;
|
|
148
|
-
},
|
|
149
|
-
get hasPreviousPage() {
|
|
150
|
-
return read("hasPreviousPage").hasPreviousPage;
|
|
151
|
-
},
|
|
152
|
-
get input() {
|
|
153
|
-
return read("input").input;
|
|
154
|
-
},
|
|
155
|
-
get isFetching() {
|
|
156
|
-
return read("isFetching").isFetching;
|
|
157
|
-
},
|
|
158
|
-
get isFetchingNextPage() {
|
|
159
|
-
return read("isFetchingNextPage").isFetchingNextPage;
|
|
160
|
-
},
|
|
161
|
-
get isFetchingPreviousPage() {
|
|
162
|
-
return read("isFetchingPreviousPage").isFetchingPreviousPage;
|
|
163
|
-
},
|
|
164
|
-
get isPending() {
|
|
165
|
-
return read("isPending").isPending;
|
|
166
|
-
},
|
|
167
|
-
get isPrevious() {
|
|
168
|
-
return read("isPrevious").isPrevious;
|
|
169
|
-
},
|
|
170
|
-
get isStale() {
|
|
171
|
-
return read("isStale").isStale;
|
|
172
|
-
},
|
|
173
|
-
get key() {
|
|
174
|
-
return read("key").key;
|
|
175
|
-
},
|
|
176
|
-
get promise() {
|
|
177
|
-
return shown();
|
|
178
|
-
},
|
|
179
|
-
refetch: () => controls().refetch(),
|
|
180
|
-
get updatedAt() {
|
|
181
|
-
return read("updatedAt").updatedAt;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
//#endregion
|
|
186
|
-
export { binding_exports, makeUnsafe, observerOptions, queryFor, subscribeUnsafe, syncUnsafe };
|
package/dist/cache.d.mts
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { QueryKey } from "./key.mjs";
|
|
2
|
-
import { DataOf, ErrorOf, InputArgs, InputOf, LoadOptions, PagedQueryFactory, QueryBehavior, QueryFactory, QueryHandle, QueryHandleWithData, ServicesOf } from "./options.mjs";
|
|
3
|
-
import { CancelledError, Query, State, query_d_exports } from "./query.mjs";
|
|
4
|
-
import { DehydrateOptions, DehydratedState, DehydratedStream, HydrateOptions } from "./hydration.mjs";
|
|
5
|
-
import { Context, Effect, Layer, Option, Scope, Stream } from "effect";
|
|
6
|
-
declare namespace cache_d_exports {
|
|
7
|
-
export { Config, Event, Filter, QueryCache, Target, TypeId, Updater, isQueryCache, layer, make };
|
|
8
|
-
}
|
|
9
|
-
export declare const TypeId = "~@raf-query/core/QueryCache";
|
|
10
|
-
export type TypeId = typeof TypeId;
|
|
11
|
-
export interface Config {
|
|
12
|
-
/** Defaults for every query; a factory's own options win. */
|
|
13
|
-
readonly defaults?: QueryBehavior;
|
|
14
|
-
/**
|
|
15
|
-
* Whether this cache runs on the server. Default: the {@link Platform}'s.
|
|
16
|
-
* Server caches don't retry, never garbage-collect, skip stale timers, and
|
|
17
|
-
* reuse cached data in `prefetch` / `ensure` / `fetch` instead of
|
|
18
|
-
* revalidating it, unless `invalidate` marked it.
|
|
19
|
-
*/
|
|
20
|
-
readonly isServer?: boolean;
|
|
21
|
-
}
|
|
22
|
-
/** Emitted on {@link QueryCache.events} for every cache change. */
|
|
23
|
-
export type Event = {
|
|
24
|
-
readonly query: Query;
|
|
25
|
-
/** `observers`: a reader subscribed or unsubscribed. */
|
|
26
|
-
readonly type: "added" | "observers" | "removed" | "updated";
|
|
27
|
-
} | {
|
|
28
|
-
/** A request failed (after retries), with this error. */
|
|
29
|
-
readonly error: unknown;
|
|
30
|
-
readonly query: Query;
|
|
31
|
-
readonly type: "failed";
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Selects cache entries: by key prefix (exact key with `exact`), then by
|
|
35
|
-
* `predicate`. Without `queryKey` every key matches, so `{}` selects the
|
|
36
|
-
* whole cache.
|
|
37
|
-
*/
|
|
38
|
-
export interface Filter {
|
|
39
|
-
readonly exact?: boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Keep only entries this returns `true` for; runs after key matching.
|
|
42
|
-
* `query.handle.input` is the input of the latest factory call, and stays
|
|
43
|
-
* `undefined` for an entry only hydrated so far; match on `query.key` to
|
|
44
|
-
* include those.
|
|
45
|
-
*/
|
|
46
|
-
readonly predicate?: (query: Query) => boolean;
|
|
47
|
-
readonly queryKey?: QueryKey;
|
|
48
|
-
}
|
|
49
|
-
/** The entries `invalidate`, `reset` and `remove` act on: one factory call, or a filter. */
|
|
50
|
-
export type Target<TFactory extends QueryFactory> = [factory: TFactory, ...args: InputArgs<TFactory>] | [filter: Filter];
|
|
51
|
-
/**
|
|
52
|
-
* A new value, or a function of the current one. `previous` is `undefined`
|
|
53
|
-
* both with no data and with `undefined` data.
|
|
54
|
-
*/
|
|
55
|
-
export type Updater<TData> = TData | ((previous: TData | undefined) => TData);
|
|
56
|
-
type Loaded<TFactory extends QueryFactory> = Effect.Effect<QueryHandleWithData<TFactory>, ErrorOf<TFactory> | CancelledError, ServicesOf<TFactory>>;
|
|
57
|
-
export interface QueryCache {
|
|
58
|
-
/** Every entry. */
|
|
59
|
-
readonly all: Effect.Effect<ReadonlyArray<Query>>;
|
|
60
|
-
/** Drop every entry and interrupt their requests. */
|
|
61
|
-
readonly clear: Effect.Effect<void>;
|
|
62
|
-
/** Snapshot the cache for transfer to another cache (server → browser). */
|
|
63
|
-
dehydrate(options?: DehydrateOptions): Effect.Effect<DehydratedState>;
|
|
64
|
-
/**
|
|
65
|
-
* Snapshot the cache, then stream entries as they start or settle, each
|
|
66
|
-
* entry once. Entries that change in the same tick share a batch, which
|
|
67
|
-
* is sent a microtask later. `updates` ends when the scope closes, after
|
|
68
|
-
* sending what changed before; `hydrateStream` is the receiving end.
|
|
69
|
-
*/
|
|
70
|
-
dehydrateStream(options?: DehydrateOptions): Effect.Effect<DehydratedStream, never, Scope.Scope>;
|
|
71
|
-
/**
|
|
72
|
-
* {@link fetch} without waiting on stale data: start a query and wait for
|
|
73
|
-
* it on a cache miss. Stale cached data succeeds immediately and
|
|
74
|
-
* revalidates in the browser. Fails if that request fails.
|
|
75
|
-
*/
|
|
76
|
-
ensure<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): Loaded<TFactory>;
|
|
77
|
-
/** {@link ensure}, loading `options.pages` pages of a paged query. */
|
|
78
|
-
ensure<TFactory extends PagedQueryFactory>(factory: TFactory, input: InputOf<TFactory>, options: LoadOptions): Loaded<TFactory>;
|
|
79
|
-
/** Cache events from the moment the stream starts. */
|
|
80
|
-
readonly events: Stream.Stream<Event>;
|
|
81
|
-
/**
|
|
82
|
-
* Start a query and succeed with a handle holding its data. Waits on a
|
|
83
|
-
* cache miss and on stale data (on the server, only invalidated data), so
|
|
84
|
-
* the data is fresh. Fails if that request fails.
|
|
85
|
-
*/
|
|
86
|
-
fetch<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): Loaded<TFactory>;
|
|
87
|
-
/** {@link fetch}, loading `options.pages` pages of a paged query. */
|
|
88
|
-
fetch<TFactory extends PagedQueryFactory>(factory: TFactory, input: InputOf<TFactory>, options: LoadOptions): Loaded<TFactory>;
|
|
89
|
-
/**
|
|
90
|
-
* Cached data for a factory call. `Some(undefined)` when the data itself
|
|
91
|
-
* is `undefined`.
|
|
92
|
-
*/
|
|
93
|
-
getData<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): Effect.Effect<Option.Option<DataOf<TFactory>>>;
|
|
94
|
-
/**
|
|
95
|
-
* The cache entry for a factory call, created if missing. Doesn't fetch,
|
|
96
|
-
* but its requests run with this fiber's services from now on. For
|
|
97
|
-
* bindings; apps use the verbs.
|
|
98
|
-
*/
|
|
99
|
-
getQuery<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): Effect.Effect<Query<TFactory>, never, ServicesOf<TFactory>>;
|
|
100
|
-
/**
|
|
101
|
-
* `getQuery`, synchronously, for renders. Leaves the entry's services as
|
|
102
|
-
* they are: the cache's, or the latest verb caller's.
|
|
103
|
-
*/
|
|
104
|
-
getQueryUnsafe<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): Query<TFactory>;
|
|
105
|
-
/**
|
|
106
|
-
* Write dehydrated entries. Existing entries only take newer data; pending
|
|
107
|
-
* entries resume the sender's request.
|
|
108
|
-
*/
|
|
109
|
-
hydrate(state: DehydratedState, options?: HydrateOptions): Effect.Effect<void>;
|
|
110
|
-
/** `hydrate` every state of a stream as it arrives (`dehydrateStream`'s `updates`). */
|
|
111
|
-
hydrateStream<E, R>(states: Stream.Stream<DehydratedState, E, R>, options?: HydrateOptions): Effect.Effect<void, E, R>;
|
|
112
|
-
/**
|
|
113
|
-
* Mark queries stale and refetch the ones being read. Succeeds once those
|
|
114
|
-
* refetches settle, so an action stays pending until fresh data is on
|
|
115
|
-
* screen.
|
|
116
|
-
*/
|
|
117
|
-
invalidate<TFactory extends QueryFactory>(...target: Target<TFactory>): Effect.Effect<void>;
|
|
118
|
-
/**
|
|
119
|
-
* How many entries have a request in flight: all of them, or those
|
|
120
|
-
* matching `filter`. For global loading indicators.
|
|
121
|
-
*/
|
|
122
|
-
isFetching(filter?: Filter): Effect.Effect<number>;
|
|
123
|
-
/** `isFetching`, synchronously, for renders. */
|
|
124
|
-
isFetchingUnsafe(filter?: Filter): number;
|
|
125
|
-
readonly isServer: boolean;
|
|
126
|
-
/**
|
|
127
|
-
* Refetch read, stale queries on window focus and reconnect while the
|
|
128
|
-
* scope is open. Mounts share one platform subscription. Does nothing on
|
|
129
|
-
* the server.
|
|
130
|
-
*/
|
|
131
|
-
readonly mount: Effect.Effect<void, never, Scope.Scope>;
|
|
132
|
-
/**
|
|
133
|
-
* Start a query without waiting and return its handle. Fresh data is
|
|
134
|
-
* reused; stale data refetches in the background (in the browser only, see
|
|
135
|
-
* {@link Config.isServer}). Errors surface where the data is read, never
|
|
136
|
-
* here.
|
|
137
|
-
*/
|
|
138
|
-
prefetch<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): Effect.Effect<QueryHandle<TFactory>, never, ServicesOf<TFactory>>;
|
|
139
|
-
/**
|
|
140
|
-
* Remove queries from the cache and interrupt their requests. Readers keep
|
|
141
|
-
* the removed entry until they render again.
|
|
142
|
-
*/
|
|
143
|
-
remove<TFactory extends QueryFactory>(...target: Target<TFactory>): Effect.Effect<void>;
|
|
144
|
-
/**
|
|
145
|
-
* Drop queries' data and errors, as if they were never fetched. Read ones
|
|
146
|
-
* suspend and refetch; succeeds once those refetches settle.
|
|
147
|
-
*/
|
|
148
|
-
reset<TFactory extends QueryFactory>(...target: Target<TFactory>): Effect.Effect<void>;
|
|
149
|
-
/** Write data for a factory call. Readers re-render without suspending. */
|
|
150
|
-
setData<TFactory extends QueryFactory>(factory: TFactory, input: InputOf<TFactory>, updater: Updater<DataOf<TFactory>>): Effect.Effect<void>;
|
|
151
|
-
/** Listen to cache events synchronously, as they happen. */
|
|
152
|
-
subscribeUnsafe(listener: (event: Event) => void): () => void;
|
|
153
|
-
readonly [TypeId]: TypeId;
|
|
154
|
-
/** A factory call's `Query.changes`. */
|
|
155
|
-
watch<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): Stream.Stream<State<DataOf<TFactory>>, never, ServicesOf<TFactory>>;
|
|
156
|
-
}
|
|
157
|
-
export declare const QueryCache: Context.Service<QueryCache, QueryCache>;
|
|
158
|
-
export declare function isQueryCache(value: unknown): value is QueryCache;
|
|
159
|
-
/** Build a cache in the current scope, with the current `Clock` and {@link Platform}. */
|
|
160
|
-
export declare const make: (config?: Config) => Effect.Effect<QueryCache, never, Scope.Scope>;
|
|
161
|
-
/** A layer that provides a {@link QueryCache} for the layer's lifetime. */
|
|
162
|
-
export declare const layer: (config?: Config) => Layer.Layer<QueryCache>;
|
|
163
|
-
//#endregion
|
|
164
|
-
export { cache_d_exports };
|
package/dist/cache.mjs
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
-
import { hashKey, matchesKey } from "./key.mjs";
|
|
3
|
-
import { listen } from "./listen.mjs";
|
|
4
|
-
import { Platform } from "./platform.mjs";
|
|
5
|
-
import { changes, emit, fetch, invalidate, isObserved, make as make$1, needsRefetch, onPlatformEvent, provide, remove, reset, revalidate, setData, setOptions, start } from "./entry.mjs";
|
|
6
|
-
import { dehydrateEntries, dehydrateStream, hydrateEntries } from "./hydration.mjs";
|
|
7
|
-
import { optionsFor } from "./options.mjs";
|
|
8
|
-
import { Clock, Context, Effect, Layer, Option, Predicate, RcRef, Stream } from "effect";
|
|
9
|
-
//#region src/cache.ts
|
|
10
|
-
/**
|
|
11
|
-
* The query cache, as an Effect service. `make` builds one in the current
|
|
12
|
-
* scope; closing the scope interrupts every request and timer it started.
|
|
13
|
-
* Everything that waits, or changes the cache, is an Effect; `getQueryUnsafe`,
|
|
14
|
-
* `isFetchingUnsafe` and `subscribeUnsafe` are for renders and external
|
|
15
|
-
* stores.
|
|
16
|
-
*
|
|
17
|
-
* Requests run with the services the cache was built with, under those of
|
|
18
|
-
* the latest verb that called for the entry. So the verbs that can start a
|
|
19
|
-
* request (`ensure`, `fetch`, `prefetch`, `getQuery`, `watch`) require the
|
|
20
|
-
* factory's {@link ServicesOf}; requests a render, a refetch or `invalidate`
|
|
21
|
-
* starts reuse the entry's last services.
|
|
22
|
-
*/
|
|
23
|
-
var cache_exports = /* @__PURE__ */ __exportAll({
|
|
24
|
-
QueryCache: () => QueryCache,
|
|
25
|
-
TypeId: () => TypeId,
|
|
26
|
-
isQueryCache: () => isQueryCache,
|
|
27
|
-
layer: () => layer,
|
|
28
|
-
make: () => make
|
|
29
|
-
});
|
|
30
|
-
const TypeId = "~@raf-query/core/QueryCache";
|
|
31
|
-
const QueryCache = /*#__PURE__*/ Context.Service(TypeId);
|
|
32
|
-
function isQueryCache(value) {
|
|
33
|
-
return Predicate.hasProperty(value, TypeId);
|
|
34
|
-
}
|
|
35
|
-
function isUpdateFunction(updater) {
|
|
36
|
-
return Predicate.isFunction(updater);
|
|
37
|
-
}
|
|
38
|
-
function typed(entry) {
|
|
39
|
-
return entry;
|
|
40
|
-
}
|
|
41
|
-
function handleOf(entry) {
|
|
42
|
-
return entry.handle;
|
|
43
|
-
}
|
|
44
|
-
function dataOf(entry) {
|
|
45
|
-
return entry.state.data;
|
|
46
|
-
}
|
|
47
|
-
function keyMatcher(filter) {
|
|
48
|
-
const prefix = filter.queryKey;
|
|
49
|
-
if (prefix === void 0) return () => true;
|
|
50
|
-
if (filter.exact) {
|
|
51
|
-
const hash = hashKey(prefix);
|
|
52
|
-
return (entry) => entry.hash === hash;
|
|
53
|
-
}
|
|
54
|
-
return (entry) => matchesKey(entry.key, prefix);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Waits for an entry's data on a cache miss, and with `fresh` also while it
|
|
58
|
-
* needs a refetch (see `Entry.needsRefetch`). Otherwise cached data returns at
|
|
59
|
-
* once, and revalidates in the background.
|
|
60
|
-
*/
|
|
61
|
-
function load(entry, pages, fresh) {
|
|
62
|
-
if (!entry.state.hasData || fresh && needsRefetch(entry)) return fetch(entry, { pages });
|
|
63
|
-
revalidate(entry);
|
|
64
|
-
return Effect.succeed(dataOf(entry));
|
|
65
|
-
}
|
|
66
|
-
/** The entry's handle as of this call, with the data `loaded` succeeds with. */
|
|
67
|
-
function withData(entry, loaded) {
|
|
68
|
-
const handle = handleOf(entry);
|
|
69
|
-
return Effect.map(loaded, (data) => ({
|
|
70
|
-
...handle,
|
|
71
|
-
data
|
|
72
|
-
}));
|
|
73
|
-
}
|
|
74
|
-
/** Settles every effect, started together. */
|
|
75
|
-
function settleAll(effects) {
|
|
76
|
-
return Effect.all(effects, {
|
|
77
|
-
concurrency: "unbounded",
|
|
78
|
-
discard: true
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
/** Build a cache in the current scope, with the current `Clock` and {@link Platform}. */
|
|
82
|
-
const make = /*#__PURE__*/ Effect.fnUntraced(function* (config = {}) {
|
|
83
|
-
const platform = yield* Platform;
|
|
84
|
-
const state = {
|
|
85
|
-
clock: yield* Clock.Clock,
|
|
86
|
-
defaults: config.defaults ?? {},
|
|
87
|
-
entries: /* @__PURE__ */ new Map(),
|
|
88
|
-
isServer: config.isServer ?? platform.isServer,
|
|
89
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
90
|
-
platform,
|
|
91
|
-
scope: yield* Effect.scope,
|
|
92
|
-
services: yield* Effect.context()
|
|
93
|
-
};
|
|
94
|
-
const entryFor = (key) => {
|
|
95
|
-
const hash = hashKey(key);
|
|
96
|
-
const existing = state.entries.get(hash);
|
|
97
|
-
if (existing) return existing;
|
|
98
|
-
const entry = make$1(state, key, hash);
|
|
99
|
-
state.entries.set(hash, entry);
|
|
100
|
-
emit(state, {
|
|
101
|
-
query: entry,
|
|
102
|
-
type: "added"
|
|
103
|
-
});
|
|
104
|
-
return entry;
|
|
105
|
-
};
|
|
106
|
-
const build = (factory, input) => {
|
|
107
|
-
const options = optionsFor(factory, input);
|
|
108
|
-
const entry = entryFor(options.queryKey);
|
|
109
|
-
setOptions(entry, options, input);
|
|
110
|
-
return entry;
|
|
111
|
-
};
|
|
112
|
-
/** `build`, for a verb: the entry's requests get the caller's services. */
|
|
113
|
-
const buildFor = (factory, input) => Effect.contextWith((caller) => {
|
|
114
|
-
const entry = build(factory, input);
|
|
115
|
-
provide(entry, caller);
|
|
116
|
-
return Effect.succeed(entry);
|
|
117
|
-
});
|
|
118
|
-
const match = (target) => {
|
|
119
|
-
const [first, input] = target;
|
|
120
|
-
if (Predicate.isFunction(first)) {
|
|
121
|
-
const entry = state.entries.get(hashKey(optionsFor(first, input).queryKey));
|
|
122
|
-
return entry ? [entry] : [];
|
|
123
|
-
}
|
|
124
|
-
const keyMatches = keyMatcher(first);
|
|
125
|
-
return [...state.entries.values()].filter((entry) => keyMatches(entry) && (first.predicate?.(entry) ?? true));
|
|
126
|
-
};
|
|
127
|
-
const subscription = yield* RcRef.make({ acquire: Effect.acquireRelease(Effect.sync(() => platform.subscribeUnsafe((event) => {
|
|
128
|
-
for (const entry of state.entries.values()) if (isObserved(entry)) onPlatformEvent(entry, event);
|
|
129
|
-
})), (unsubscribe) => Effect.sync(unsubscribe)) });
|
|
130
|
-
const subscribeUnsafe = (listener) => {
|
|
131
|
-
state.listeners.add(listener);
|
|
132
|
-
return () => {
|
|
133
|
-
state.listeners.delete(listener);
|
|
134
|
-
};
|
|
135
|
-
};
|
|
136
|
-
const getQueryUnsafe = (factory, ...args) => typed(build(factory, args[0]));
|
|
137
|
-
const isFetchingUnsafe = (filter) => (filter ? match([filter]) : [...state.entries.values()]).filter((entry) => entry.state.isFetching).length;
|
|
138
|
-
return {
|
|
139
|
-
all: Effect.sync(() => [...state.entries.values()]),
|
|
140
|
-
clear: Effect.sync(() => {
|
|
141
|
-
for (const entry of state.entries.values()) remove(entry);
|
|
142
|
-
}),
|
|
143
|
-
dehydrate: (options = {}) => Effect.sync(() => dehydrateEntries(state.entries.values(), options)),
|
|
144
|
-
dehydrateStream: (options = {}) => dehydrateStream(() => state.entries.values(), subscribeUnsafe, options),
|
|
145
|
-
ensure: (factory, input, options) => Effect.flatMap(buildFor(factory, input), (entry) => withData(entry, load(entry, options?.pages, false))),
|
|
146
|
-
events: listen(subscribeUnsafe),
|
|
147
|
-
fetch: (factory, input, options) => Effect.flatMap(buildFor(factory, input), (entry) => withData(entry, load(entry, options?.pages, true))),
|
|
148
|
-
getData: (factory, ...args) => Effect.sync(() => {
|
|
149
|
-
const options = optionsFor(factory, args[0]);
|
|
150
|
-
const entry = state.entries.get(hashKey(options.queryKey));
|
|
151
|
-
return entry?.state.hasData ? Option.some(dataOf(entry)) : Option.none();
|
|
152
|
-
}),
|
|
153
|
-
getQuery: (factory, ...args) => Effect.map(buildFor(factory, args[0]), typed),
|
|
154
|
-
getQueryUnsafe,
|
|
155
|
-
hydrate: (dehydrated, options = {}) => Effect.sync(() => hydrateEntries(entryFor, dehydrated, options)),
|
|
156
|
-
hydrateStream: (states, options = {}) => Stream.runForEach(states, (dehydrated) => Effect.sync(() => hydrateEntries(entryFor, dehydrated, options))),
|
|
157
|
-
invalidate: (...target) => Effect.suspend(() => settleAll(match(target).map(invalidate))),
|
|
158
|
-
isFetching: (filter) => Effect.sync(() => isFetchingUnsafe(filter)),
|
|
159
|
-
isFetchingUnsafe,
|
|
160
|
-
isServer: state.isServer,
|
|
161
|
-
mount: state.isServer ? Effect.void : Effect.asVoid(RcRef.get(subscription)),
|
|
162
|
-
prefetch: (factory, ...args) => Effect.map(buildFor(factory, args[0]), (entry) => {
|
|
163
|
-
if (entry.state.hasData) revalidate(entry);
|
|
164
|
-
else start(entry);
|
|
165
|
-
return handleOf(entry);
|
|
166
|
-
}),
|
|
167
|
-
remove: (...target) => Effect.sync(() => {
|
|
168
|
-
for (const entry of match(target)) remove(entry);
|
|
169
|
-
}),
|
|
170
|
-
reset: (...target) => Effect.suspend(() => settleAll(match(target).map(reset))),
|
|
171
|
-
setData: (factory, input, updater) => Effect.sync(() => {
|
|
172
|
-
const entry = build(factory, input);
|
|
173
|
-
const next = isUpdateFunction(updater) ? updater(entry.state.hasData ? dataOf(entry) : void 0) : updater;
|
|
174
|
-
setData(entry, next);
|
|
175
|
-
}),
|
|
176
|
-
subscribeUnsafe,
|
|
177
|
-
[TypeId]: TypeId,
|
|
178
|
-
watch: (factory, ...args) => Stream.unwrap(Effect.map(buildFor(factory, args[0]), (entry) => changes(entry)))
|
|
179
|
-
};
|
|
180
|
-
});
|
|
181
|
-
/** A layer that provides a {@link QueryCache} for the layer's lifetime. */
|
|
182
|
-
const layer = (config) => Layer.effect(QueryCache, make(config));
|
|
183
|
-
//#endregion
|
|
184
|
-
export { QueryCache, TypeId, cache_exports, isQueryCache, layer, make };
|