@raf-query/core 0.11.0 → 0.12.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 +18 -2
- package/README.md +28 -18
- package/dist/client.d.mts +132 -119
- package/dist/client.mjs +59 -162
- package/dist/entry.d.mts +67 -148
- package/dist/entry.mjs +303 -474
- package/dist/features/focus-refetch/focus-refetch.d.mts +15 -0
- package/dist/features/focus-refetch/focus-refetch.mjs +18 -0
- package/dist/features/polling/polling.d.mts +12 -0
- package/dist/features/polling/polling.mjs +35 -0
- package/dist/features/reconnect-refetch/reconnect-refetch.d.mts +15 -0
- package/dist/features/reconnect-refetch/reconnect-refetch.mjs +14 -0
- package/dist/features/shared.mjs +13 -0
- package/dist/guards.mjs +23 -0
- package/dist/hydration/hydration.d.mts +73 -0
- package/dist/hydration/hydration.mjs +128 -0
- package/dist/index.d.mts +18 -13
- package/dist/index.mjs +15 -13
- package/dist/infinite/infinite.d.mts +71 -0
- package/dist/infinite/infinite.mjs +88 -0
- package/dist/internal.d.mts +13 -0
- package/dist/internal.mjs +7 -0
- package/dist/keys/keys.d.mts +46 -0
- package/dist/keys/keys.mjs +53 -0
- package/dist/keys/targets.d.mts +56 -0
- package/dist/keys/targets.mjs +85 -0
- package/dist/links/retry/retry.d.mts +26 -0
- package/dist/links/retry/retry.mjs +51 -0
- package/dist/links/timeout/timeout.d.mts +9 -0
- package/dist/links/timeout/timeout.mjs +27 -0
- package/dist/load.d.mts +124 -0
- package/dist/load.mjs +165 -0
- package/dist/options.d.mts +112 -338
- package/dist/options.mjs +25 -36
- package/dist/presets.d.mts +29 -0
- package/dist/presets.mjs +25 -0
- package/dist/root.d.mts +82 -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 +13 -17
- package/dist/thenable.mjs +25 -6
- package/dist/verbs.d.mts +109 -0
- package/dist/verbs.mjs +87 -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 -93
- package/dist/controls.mjs +0 -69
- 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/root.d.mts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//#region src/root.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Root types, the way tRPC v12 types a client from its links: each link and
|
|
4
|
+
* feature declares the `meta` fields it reads, a client's `meta` is what its
|
|
5
|
+
* parts declare, and builders bound to a root (`bindQueryOptions`) carry it
|
|
6
|
+
* as a phantom on the options they build.
|
|
7
|
+
*/
|
|
8
|
+
/** A query's `meta` when its builder isn't bound to a root. */
|
|
9
|
+
export type QueryMeta = Readonly<Record<string, unknown>>;
|
|
10
|
+
/** What a link or feature declares: the `meta` fields it reads. */
|
|
11
|
+
export interface Decl {
|
|
12
|
+
readonly meta?: object;
|
|
13
|
+
}
|
|
14
|
+
/** A root's types. */
|
|
15
|
+
export interface RootTypes {
|
|
16
|
+
readonly meta: object;
|
|
17
|
+
}
|
|
18
|
+
/** The root of the unbound builders. A type only, never a value. */
|
|
19
|
+
export interface DefaultTypes {
|
|
20
|
+
readonly "~default": true;
|
|
21
|
+
readonly meta: QueryMeta;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The root of a client typed only as `QueryClient`: any root. The one way
|
|
25
|
+
* an invariant phantom takes every root.
|
|
26
|
+
*/
|
|
27
|
+
export type AnyRoot = any;
|
|
28
|
+
/** Whether `TTypes` is {@link AnyRoot}. */
|
|
29
|
+
export type IsAnyRoot<TTypes> = 0 extends 1 & TTypes ? true : false;
|
|
30
|
+
/**
|
|
31
|
+
* Phantom carrier of a part's declaration, `undefined` at runtime.
|
|
32
|
+
* Invariant, so a part that declares nothing doesn't absorb the
|
|
33
|
+
* declarations of the others in an array.
|
|
34
|
+
*/
|
|
35
|
+
export interface Declares<TDecl extends Decl> {
|
|
36
|
+
readonly "~decl"?: {
|
|
37
|
+
readonly in: (decl: TDecl) => void;
|
|
38
|
+
readonly out: TDecl;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Any link or feature, whatever it declares. */
|
|
42
|
+
export interface AnyPart {
|
|
43
|
+
readonly "~decl"?: {
|
|
44
|
+
readonly in: (decl: never) => void;
|
|
45
|
+
readonly out: unknown;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The `meta` a declaration adds to its root. Every declared key is present
|
|
50
|
+
* and may be `undefined`: roots of all-optional metas would be assignable to
|
|
51
|
+
* one another. Queries pass a `Partial` of it.
|
|
52
|
+
*/
|
|
53
|
+
export type MetaOfDecl<TDecl> = TDecl extends {
|
|
54
|
+
readonly meta?: infer TMeta extends object;
|
|
55
|
+
} ? Present<TMeta> : unknown;
|
|
56
|
+
/**
|
|
57
|
+
* Every key of `T` required, its value possibly `undefined`. Mapping over
|
|
58
|
+
* `Exclude<keyof T, never>` rather than `keyof T` drops the modifiers
|
|
59
|
+
* without dropping `undefined`, which `-?` would.
|
|
60
|
+
*/
|
|
61
|
+
type Present<T> = { readonly [K in Exclude<keyof T, never>]: T[K] | undefined; };
|
|
62
|
+
type DeclOf<TPart> = TPart extends {
|
|
63
|
+
readonly "~decl"?: {
|
|
64
|
+
readonly out: infer TDecl;
|
|
65
|
+
};
|
|
66
|
+
} ? TDecl : never;
|
|
67
|
+
type Intersect<TUnion> = (TUnion extends unknown ? (value: TUnion) => void : never) extends ((value: infer TIntersection) => void) ? TIntersection : never;
|
|
68
|
+
type Flat<T> = { readonly [K in keyof T]: T[K]; };
|
|
69
|
+
/** A root's `meta`: what its links and features declare. */
|
|
70
|
+
export type MetaOf<TParts extends ReadonlyArray<AnyPart>> = [DeclOf<TParts[number]>] extends [never] ? Flat<unknown> : Flat<Intersect<MetaOfDecl<DeclOf<TParts[number]>>>>;
|
|
71
|
+
declare const rootBrand: unique symbol;
|
|
72
|
+
/** Phantom carrier of the root a factory's builder is bound to. Never set at runtime. */
|
|
73
|
+
export interface RootTag<TTypes extends RootTypes> {
|
|
74
|
+
readonly [rootBrand]: TTypes;
|
|
75
|
+
}
|
|
76
|
+
/** A root's types, from a client or from a function that makes one. */
|
|
77
|
+
export type RootOf<T> = T extends ((...args: never) => infer TClient) ? RootOf<TClient> : T extends {
|
|
78
|
+
readonly "~types"?: {
|
|
79
|
+
readonly root: (types: infer TTypes extends RootTypes) => unknown;
|
|
80
|
+
};
|
|
81
|
+
} ? TTypes : never;
|
|
82
|
+
//#endregion
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/structural-sharing/structural-sharing.ts
|
|
2
|
+
function isPlainArray(value) {
|
|
3
|
+
return Array.isArray(value) && value.length === Object.keys(value).length;
|
|
4
|
+
}
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
if (typeof value !== "object" || value === null) return false;
|
|
7
|
+
const prototype = Object.getPrototypeOf(value);
|
|
8
|
+
return prototype === Object.prototype || prototype === null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `next`, reusing every part of `previous` that is deeply equal to it, so
|
|
12
|
+
* unchanged objects keep their identity and memoized readers skip work.
|
|
13
|
+
*/
|
|
14
|
+
function replaceEqualDeep(previous, next) {
|
|
15
|
+
if (Object.is(previous, next)) return previous;
|
|
16
|
+
const arrays = isPlainArray(previous) && isPlainArray(next);
|
|
17
|
+
if (!arrays && !(isPlainObject(previous) && isPlainObject(next))) return next;
|
|
18
|
+
const before = previous;
|
|
19
|
+
const after = next;
|
|
20
|
+
const names = Object.keys(after);
|
|
21
|
+
let same = names.length === Object.keys(before).length;
|
|
22
|
+
const values = names.map((name) => {
|
|
23
|
+
const own = Object.hasOwn(before, name);
|
|
24
|
+
const value = replaceEqualDeep(own ? before[name] : void 0, after[name]);
|
|
25
|
+
if (!own || value !== before[name]) same = false;
|
|
26
|
+
return value;
|
|
27
|
+
});
|
|
28
|
+
if (same) return previous;
|
|
29
|
+
return arrays ? values : Object.fromEntries(names.map((name, index) => [name, values[index]]));
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { replaceEqualDeep };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/subscribe.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The kernel's one subscription primitive: `subscribe(listener, { signal })`.
|
|
4
|
+
* Unsubscribe by aborting the signal, or by calling what it returns. There
|
|
5
|
+
* is no error channel: errors live in entry state.
|
|
6
|
+
*/
|
|
7
|
+
export interface SubscribeOptions {
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
export interface Listeners<TEvent> {
|
|
11
|
+
emit(event: TEvent): void;
|
|
12
|
+
readonly size: number;
|
|
13
|
+
subscribe(listener: (event: TEvent) => void, options?: SubscribeOptions): () => void;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/subscribe.ts
|
|
2
|
+
function listeners(onSizeChange) {
|
|
3
|
+
const set = /* @__PURE__ */ new Set();
|
|
4
|
+
return {
|
|
5
|
+
emit(event) {
|
|
6
|
+
for (const item of set) item.listener(event);
|
|
7
|
+
},
|
|
8
|
+
get size() {
|
|
9
|
+
return set.size;
|
|
10
|
+
},
|
|
11
|
+
subscribe(listener, options) {
|
|
12
|
+
const signal = options?.signal;
|
|
13
|
+
if (signal?.aborted) return () => {};
|
|
14
|
+
const item = { listener };
|
|
15
|
+
set.add(item);
|
|
16
|
+
onSizeChange?.(set.size);
|
|
17
|
+
const unsubscribe = () => {
|
|
18
|
+
if (set.delete(item)) {
|
|
19
|
+
signal?.removeEventListener("abort", unsubscribe);
|
|
20
|
+
onSizeChange?.(set.size);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
signal?.addEventListener("abort", unsubscribe, { once: true });
|
|
24
|
+
return unsubscribe;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { listeners };
|
package/dist/thenable.d.mts
CHANGED
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
//#region src/thenable.d.ts
|
|
2
2
|
/**
|
|
3
|
-
* Promises that carry their own result
|
|
4
|
-
* before suspending (`status`, `value`, `reason`). A settled one
|
|
3
|
+
* Promises that carry their own result in the fields React's `use()` reads
|
|
4
|
+
* before suspending (`status`, `value`, `reason`). A settled one is read
|
|
5
5
|
* synchronously, so React renders it without a fallback.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
readonly status: "pending";
|
|
9
|
-
}
|
|
10
|
-
interface FulfilledStatus<T> {
|
|
11
|
-
readonly status: "fulfilled";
|
|
12
|
-
readonly value: T;
|
|
13
|
-
}
|
|
14
|
-
interface RejectedStatus {
|
|
7
|
+
export type Thenable<T> = Promise<T> & ({
|
|
15
8
|
readonly reason: unknown;
|
|
16
9
|
readonly status: "rejected";
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export type Thenable<T> = Promise<T> & (FulfilledStatus<T> | PendingStatus | RejectedStatus);
|
|
10
|
+
} | {
|
|
11
|
+
readonly status: "fulfilled";
|
|
12
|
+
readonly value: T;
|
|
13
|
+
} | {
|
|
14
|
+
readonly status: "pending";
|
|
15
|
+
});
|
|
24
16
|
export declare function fulfilled<T>(value: T): Thenable<T>;
|
|
17
|
+
export declare function rejected<T>(reason: unknown): Thenable<T>;
|
|
25
18
|
export interface Pending<T> {
|
|
26
19
|
readonly promise: Thenable<T>;
|
|
27
20
|
reject(reason: unknown): void;
|
|
28
21
|
resolve(value: T): void;
|
|
29
22
|
}
|
|
23
|
+
/** A pending thenable that settles in place, so `use()` resumes on it. */
|
|
30
24
|
export declare function pending<T>(): Pending<T>;
|
|
25
|
+
/** `thenable` with `map` applied to its value, cached per source thenable. */
|
|
26
|
+
export declare function mapThenable<T, U>(cache: WeakMap<Thenable<unknown>, Thenable<unknown>>, thenable: Thenable<T>, map: (value: T) => U): Thenable<U>;
|
|
31
27
|
//#endregion
|
package/dist/thenable.mjs
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
//#region src/thenable.ts
|
|
2
|
-
/**
|
|
3
|
-
* Promises that carry their own result, in the fields React's `use()` reads
|
|
4
|
-
* before suspending (`status`, `value`, `reason`). A settled one can be read
|
|
5
|
-
* synchronously, so React renders it without a fallback.
|
|
6
|
-
*/
|
|
7
2
|
const noop = () => {};
|
|
8
3
|
function fulfilled(value) {
|
|
9
4
|
return Object.assign(Promise.resolve(value), {
|
|
@@ -11,6 +6,15 @@ function fulfilled(value) {
|
|
|
11
6
|
value
|
|
12
7
|
});
|
|
13
8
|
}
|
|
9
|
+
function rejected(reason) {
|
|
10
|
+
const promise = Promise.reject(reason);
|
|
11
|
+
promise.catch(noop);
|
|
12
|
+
return Object.assign(promise, {
|
|
13
|
+
reason,
|
|
14
|
+
status: "rejected"
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** A pending thenable that settles in place, so `use()` resumes on it. */
|
|
14
18
|
function pending() {
|
|
15
19
|
const deferred = Promise.withResolvers();
|
|
16
20
|
const promise = Object.assign(deferred.promise, { status: "pending" });
|
|
@@ -33,5 +37,20 @@ function pending() {
|
|
|
33
37
|
}
|
|
34
38
|
};
|
|
35
39
|
}
|
|
40
|
+
/** `thenable` with `map` applied to its value, cached per source thenable. */
|
|
41
|
+
function mapThenable(cache, thenable, map) {
|
|
42
|
+
const cached = cache.get(thenable);
|
|
43
|
+
if (cached) return cached;
|
|
44
|
+
let mapped;
|
|
45
|
+
if (thenable.status === "fulfilled") mapped = fulfilled(map(thenable.value));
|
|
46
|
+
else if (thenable.status === "rejected") mapped = rejected(thenable.reason);
|
|
47
|
+
else {
|
|
48
|
+
const next = pending();
|
|
49
|
+
thenable.then((value) => next.resolve(map(value)), (error) => next.reject(error));
|
|
50
|
+
mapped = next.promise;
|
|
51
|
+
}
|
|
52
|
+
cache.set(thenable, mapped);
|
|
53
|
+
return mapped;
|
|
54
|
+
}
|
|
36
55
|
//#endregion
|
|
37
|
-
export { fulfilled, pending };
|
|
56
|
+
export { fulfilled, mapThenable, pending, rejected };
|
package/dist/verbs.d.mts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Group } from "./keys/keys.mjs";
|
|
2
|
+
import { IsAnyRoot, RootTypes } from "./root.mjs";
|
|
3
|
+
import { AnyInvalidation, AnyTarget, ParamArgs, Target } from "./keys/targets.mjs";
|
|
4
|
+
import { InputArgs, InputOf, QueryFactory, RootOfFactory } from "./options.mjs";
|
|
5
|
+
import { QueryClient } from "./client.mjs";
|
|
6
|
+
import { InfiniteData } from "./infinite/infinite.mjs";
|
|
7
|
+
//#region src/verbs.d.ts
|
|
8
|
+
type DeepPartial<T> = T extends object ? { readonly [TName in keyof T]?: DeepPartial<T[TName]>; } : T;
|
|
9
|
+
/**
|
|
10
|
+
* What `invalidateMany(factory, partial)` takes: part of the factory's
|
|
11
|
+
* input, whatever its key looks like.
|
|
12
|
+
*/
|
|
13
|
+
export type PartialInput<TFactory extends QueryFactory> = DeepPartial<InputOf<TFactory>>;
|
|
14
|
+
/**
|
|
15
|
+
* The clients a factory's verbs take: its root's, or any client for a
|
|
16
|
+
* factory from an unbound builder.
|
|
17
|
+
*/
|
|
18
|
+
export type ClientFor<TFactory extends QueryFactory> = QueryClient<RootOfFactory<TFactory>>;
|
|
19
|
+
/**
|
|
20
|
+
* A target a client's root can match: its `where` reads that root's `meta`.
|
|
21
|
+
* A client typed only as `QueryClient` (from `useQueryClient()`, say) may
|
|
22
|
+
* be of any root, so it takes any target.
|
|
23
|
+
*/
|
|
24
|
+
export type TargetFor<TTypes extends RootTypes, TParam> = IsAnyRoot<TTypes> extends true ? Target<never, TParam> : Target<NoInfer<TTypes["meta"]>, TParam>;
|
|
25
|
+
type ParamOf<TTarget> = TTarget extends {
|
|
26
|
+
readonly where: (query: never, param: infer TParam) => boolean;
|
|
27
|
+
} ? TParam : never;
|
|
28
|
+
/** A factory, if the client's root is its root; else nothing fits. */
|
|
29
|
+
type FactoryFor<TTypes extends RootTypes, TFactory extends QueryFactory> = [QueryClient<TTypes>] extends [ClientFor<TFactory>] ? TFactory : never;
|
|
30
|
+
/** A target on its own, if it needs no parameter; else with its parameter. */
|
|
31
|
+
type BareTarget<TTypes extends RootTypes, TParam> = [] extends ParamArgs<TParam> ? TargetFor<TTypes, TParam> : readonly [TargetFor<TTypes, TParam>, TParam];
|
|
32
|
+
type AnyItem = AnyTarget | Group | QueryFactory | readonly [AnyTarget | QueryFactory, unknown];
|
|
33
|
+
/**
|
|
34
|
+
* What one element of a target list must be, given what was passed: a
|
|
35
|
+
* group, a factory of the client's root, a factory with a partial input, a
|
|
36
|
+
* target, or a target with its parameter.
|
|
37
|
+
*/
|
|
38
|
+
type Item<TTypes extends RootTypes, TItem> = TItem extends Group ? Group : TItem extends QueryFactory ? FactoryFor<TTypes, TItem> : TItem extends AnyTarget ? BareTarget<TTypes, ParamOf<TItem>> : TItem extends readonly [infer TFirst, unknown] ? TFirst extends QueryFactory ? readonly [FactoryFor<TTypes, TFirst>, PartialInput<TFirst>] : TFirst extends AnyTarget ? readonly [TargetFor<TTypes, ParamOf<TFirst>>, ParamOf<TFirst>] : AnyItem : AnyItem;
|
|
39
|
+
/**
|
|
40
|
+
* Several targets for one verb call, each checked on its own: a group, a
|
|
41
|
+
* factory, `[factory, partial]`, a target, or `[target, param]`.
|
|
42
|
+
*/
|
|
43
|
+
export type TargetList<TTypes extends RootTypes, TItems extends ReadonlyArray<unknown>> = { readonly [TIndex in keyof TItems]: Item<TTypes, TItems[TIndex]>; };
|
|
44
|
+
/** The data `setData` writes: plain for a query, paged for an infinite one. */
|
|
45
|
+
type WritableData<TFactory extends QueryFactory> = ReturnType<TFactory> extends {
|
|
46
|
+
readonly select?: (data: infer TData) => unknown;
|
|
47
|
+
} ? TData : InfiniteData<unknown>;
|
|
48
|
+
/**
|
|
49
|
+
* Mark one query stale: a factory and its full input. If it's read, it
|
|
50
|
+
* refetches; resolves when that settles.
|
|
51
|
+
*/
|
|
52
|
+
export declare function invalidateOne<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Mark queries stale: a factory's (optionally only those whose input
|
|
55
|
+
* matches `partial`), a group's (keys that start with its path, whether
|
|
56
|
+
* the group built them or not), or those a target's `where` picks. Read
|
|
57
|
+
* queries refetch now; the rest when next read. Resolves when the
|
|
58
|
+
* refetches settle.
|
|
59
|
+
*
|
|
60
|
+
* Pass a list to invalidate several targets in one call: each query they
|
|
61
|
+
* cover refetches once, and the refetches run side by side. Awaiting one
|
|
62
|
+
* call after another runs them one after another instead.
|
|
63
|
+
*
|
|
64
|
+
* ```ts
|
|
65
|
+
* await invalidateMany(client, [todos, [issuesQuery, { status: "open" }], [ownedBy, "billing"]]);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function invalidateMany<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, partial?: PartialInput<TFactory>): Promise<void>;
|
|
69
|
+
export declare function invalidateMany(client: QueryClient, group: Group): Promise<void>;
|
|
70
|
+
export declare function invalidateMany<TTypes extends RootTypes, TParam = never>(client: QueryClient<TTypes>, target: TargetFor<TTypes, TParam>, ...param: ParamArgs<NoInfer<TParam>>): Promise<void>;
|
|
71
|
+
export declare function invalidateMany<TTypes extends RootTypes, const TItems extends ReadonlyArray<unknown>>(client: QueryClient<TTypes>, targets: TargetList<TTypes, TItems>): Promise<void>;
|
|
72
|
+
/** Mark every query stale; read ones refetch. For sign-out, or after any write. */
|
|
73
|
+
export declare function invalidateAll(client: QueryClient): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Drop one query's data and error. If it's read, it suspends and refetches;
|
|
76
|
+
* resolves when that settles.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resetOne<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): Promise<void>;
|
|
79
|
+
/** {@link resetOne} for every query a target, or a list of them, covers. */
|
|
80
|
+
export declare function resetMany<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, partial?: PartialInput<TFactory>): Promise<void>;
|
|
81
|
+
export declare function resetMany(client: QueryClient, group: Group): Promise<void>;
|
|
82
|
+
export declare function resetMany<TTypes extends RootTypes, TParam = never>(client: QueryClient<TTypes>, target: TargetFor<TTypes, TParam>, ...param: ParamArgs<NoInfer<TParam>>): Promise<void>;
|
|
83
|
+
export declare function resetMany<TTypes extends RootTypes, const TItems extends ReadonlyArray<unknown>>(client: QueryClient<TTypes>, targets: TargetList<TTypes, TItems>): Promise<void>;
|
|
84
|
+
/** {@link resetOne} for every query. */
|
|
85
|
+
export declare function resetAll(client: QueryClient): Promise<void>;
|
|
86
|
+
/** Remove one query from the cache, aborting its request. */
|
|
87
|
+
export declare function removeOne<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): void;
|
|
88
|
+
/** {@link removeOne} for every query a target, or a list of them, covers. */
|
|
89
|
+
export declare function removeMany<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, partial?: PartialInput<TFactory>): void;
|
|
90
|
+
export declare function removeMany(client: QueryClient, group: Group): void;
|
|
91
|
+
export declare function removeMany<TTypes extends RootTypes, TParam = never>(client: QueryClient<TTypes>, target: TargetFor<TTypes, TParam>, ...param: ParamArgs<NoInfer<TParam>>): void;
|
|
92
|
+
export declare function removeMany<TTypes extends RootTypes, const TItems extends ReadonlyArray<unknown>>(client: QueryClient<TTypes>, targets: TargetList<TTypes, TItems>): void;
|
|
93
|
+
/** Remove every query, aborting their requests. */
|
|
94
|
+
export declare function removeAll(client: QueryClient): void;
|
|
95
|
+
/** New data for `setData`, or a function from the current data to it. */
|
|
96
|
+
type Updater<TFactory extends QueryFactory> = ((previous: WritableData<TFactory> | undefined) => WritableData<TFactory>) | WritableData<TFactory>;
|
|
97
|
+
/**
|
|
98
|
+
* Write a query's data, as if a request had just returned it. `updater`
|
|
99
|
+
* gets the current data, or `undefined` if there is none. A factory that
|
|
100
|
+
* takes no input (or an optional one) skips the input:
|
|
101
|
+
* `setData(client, todosQuery, updater)`.
|
|
102
|
+
*/
|
|
103
|
+
export declare function setData<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...updater: [] extends Parameters<TFactory> ? [updater: Updater<TFactory>] : never): void;
|
|
104
|
+
export declare function setData<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, input: Parameters<TFactory> extends [] ? never : Parameters<TFactory>[0], updater: Updater<TFactory>): void;
|
|
105
|
+
/** A query's cached data (before `select`), or `undefined`. */
|
|
106
|
+
export declare function getData<TFactory extends QueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): WritableData<TFactory> | undefined;
|
|
107
|
+
/** How many queries have a request in flight, optionally only a factory's, a group's or a target's. */
|
|
108
|
+
export declare function isFetching(client: QueryClient, target?: AnyInvalidation): number;
|
|
109
|
+
//#endregion
|
package/dist/verbs.mjs
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { isArray, isFunction } from "./guards.mjs";
|
|
2
|
+
import { hashKey } from "./keys/keys.mjs";
|
|
3
|
+
import { entryStamp, matches, stampFor } from "./keys/targets.mjs";
|
|
4
|
+
import { optionsOf } from "./options.mjs";
|
|
5
|
+
import { entryOf, invalidate, remove, reset, write } from "./entry.mjs";
|
|
6
|
+
//#region src/verbs.ts
|
|
7
|
+
function hashOf(factory, input) {
|
|
8
|
+
return hashKey(optionsOf(factory, input).queryKey);
|
|
9
|
+
}
|
|
10
|
+
/** A stamp per target: one target and its argument, or each of a list. */
|
|
11
|
+
function stampsOf(targets, arg, at) {
|
|
12
|
+
return (isArray(targets) ? targets : [[targets, arg]]).map((item) => isArray(item) ? stampFor(item[0], item[1], at) : stampFor(item, void 0, at));
|
|
13
|
+
}
|
|
14
|
+
function entriesMatching(client, stamps) {
|
|
15
|
+
return [...client["~entries"].values()].filter((entry) => stamps.some((stamp) => matches(entry, stamp)));
|
|
16
|
+
}
|
|
17
|
+
function settle(outcomes) {
|
|
18
|
+
return Promise.all(outcomes).then(() => {});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Mark one query stale: a factory and its full input. If it's read, it
|
|
22
|
+
* refetches; resolves when that settles.
|
|
23
|
+
*/
|
|
24
|
+
function invalidateOne(client, factory, ...input) {
|
|
25
|
+
return invalidate(client, (at) => [entryStamp(hashOf(factory, input[0]), at)]);
|
|
26
|
+
}
|
|
27
|
+
function invalidateMany(client, target, partial) {
|
|
28
|
+
return invalidate(client, (at) => stampsOf(target, partial, at));
|
|
29
|
+
}
|
|
30
|
+
/** Mark every query stale; read ones refetch. For sign-out, or after any write. */
|
|
31
|
+
function invalidateAll(client) {
|
|
32
|
+
return invalidate(client, (at) => [{
|
|
33
|
+
at,
|
|
34
|
+
kind: "all"
|
|
35
|
+
}]);
|
|
36
|
+
}
|
|
37
|
+
function oneEntry(client, factory, input) {
|
|
38
|
+
return client["~entries"].get(hashOf(factory, input));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Drop one query's data and error. If it's read, it suspends and refetches;
|
|
42
|
+
* resolves when that settles.
|
|
43
|
+
*/
|
|
44
|
+
function resetOne(client, factory, ...input) {
|
|
45
|
+
const entry = oneEntry(client, factory, input[0]);
|
|
46
|
+
return settle(entry ? [reset(entry)] : []);
|
|
47
|
+
}
|
|
48
|
+
function resetMany(client, target, partial) {
|
|
49
|
+
return settle(entriesMatching(client, stampsOf(target, partial, 0)).map(reset));
|
|
50
|
+
}
|
|
51
|
+
/** {@link resetOne} for every query. */
|
|
52
|
+
function resetAll(client) {
|
|
53
|
+
return settle([...client["~entries"].values()].map(reset));
|
|
54
|
+
}
|
|
55
|
+
/** Remove one query from the cache, aborting its request. */
|
|
56
|
+
function removeOne(client, factory, ...input) {
|
|
57
|
+
const entry = oneEntry(client, factory, input[0]);
|
|
58
|
+
if (entry) remove(entry);
|
|
59
|
+
}
|
|
60
|
+
function removeMany(client, target, partial) {
|
|
61
|
+
for (const entry of entriesMatching(client, stampsOf(target, partial, 0))) remove(entry);
|
|
62
|
+
}
|
|
63
|
+
/** Remove every query, aborting their requests. */
|
|
64
|
+
function removeAll(client) {
|
|
65
|
+
for (const entry of client["~entries"].values()) remove(entry);
|
|
66
|
+
}
|
|
67
|
+
function setData(client, factory, ...args) {
|
|
68
|
+
const [input, updater] = args.length < 2 ? [void 0, args[0]] : args;
|
|
69
|
+
const entry = entryOf(client, factory, input);
|
|
70
|
+
const previous = entry.state.hasData ? entry.state.data : void 0;
|
|
71
|
+
const data = isFunction(updater) ? updater(previous) : updater;
|
|
72
|
+
write(entry, data, client.clock.now(), ++client["~seq"]);
|
|
73
|
+
}
|
|
74
|
+
/** A query's cached data (before `select`), or `undefined`. */
|
|
75
|
+
function getData(client, factory, ...input) {
|
|
76
|
+
const entry = oneEntry(client, factory, input[0]);
|
|
77
|
+
return entry?.state.hasData ? entry.state.data : void 0;
|
|
78
|
+
}
|
|
79
|
+
/** How many queries have a request in flight, optionally only a factory's, a group's or a target's. */
|
|
80
|
+
function isFetching(client, target) {
|
|
81
|
+
return entriesMatching(client, [target === void 0 ? {
|
|
82
|
+
at: 0,
|
|
83
|
+
kind: "all"
|
|
84
|
+
} : stampFor(target, void 0, 0)]).filter((entry) => entry.state.isFetching).length;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { getData, invalidateAll, invalidateMany, invalidateOne, isFetching, removeAll, removeMany, removeOne, resetAll, resetMany, resetOne, setData };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raf-query/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Framework-agnostic core of raf-query, a render-as-you-fetch query library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"query",
|
|
@@ -31,18 +31,22 @@
|
|
|
31
31
|
"type": "module",
|
|
32
32
|
"sideEffects": false,
|
|
33
33
|
"exports": {
|
|
34
|
-
".": "./dist/index.mjs"
|
|
35
|
-
|
|
36
|
-
"peerDependencies": {
|
|
37
|
-
"effect": "^4.0.0"
|
|
34
|
+
".": "./dist/index.mjs",
|
|
35
|
+
"./internal": "./dist/internal.mjs"
|
|
38
36
|
},
|
|
39
37
|
"devDependencies": {
|
|
40
|
-
"@
|
|
38
|
+
"@testing-library/react": "16.3.3",
|
|
41
39
|
"@types/node": "26.6.4",
|
|
40
|
+
"@types/react": "19.3.0",
|
|
41
|
+
"@types/react-dom": "19.3.0",
|
|
42
42
|
"@vitest/coverage-v8": "5.0.1",
|
|
43
|
-
"
|
|
43
|
+
"fast-check": "4.10.2",
|
|
44
|
+
"happy-dom": "21.0.1",
|
|
45
|
+
"react": "19.3.0",
|
|
46
|
+
"react-dom": "19.3.0",
|
|
44
47
|
"typescript": "7.0.2",
|
|
45
|
-
"vite-plus": "1.0.0"
|
|
48
|
+
"vite-plus": "1.0.0",
|
|
49
|
+
"vitest": "5.0.1"
|
|
46
50
|
},
|
|
47
51
|
"publishConfig": {
|
|
48
52
|
"access": "public",
|
|
@@ -54,7 +58,7 @@
|
|
|
54
58
|
"scripts": {
|
|
55
59
|
"build": "vp pack",
|
|
56
60
|
"test": "vp test",
|
|
57
|
-
"typecheck": "tsc",
|
|
61
|
+
"typecheck": "tsc && tsc -p tsconfig.no-node.json",
|
|
58
62
|
"publish:npm": "pnpm pack --out package.tgz && npm publish package.tgz --access public && rm package.tgz"
|
|
59
63
|
}
|
|
60
64
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
//#region \0rolldown/runtime.js
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __exportAll = (all, no_symbols) => {
|
|
4
|
-
let target = {};
|
|
5
|
-
for (var name in all) __defProp(target, name, {
|
|
6
|
-
get: all[name],
|
|
7
|
-
enumerable: true
|
|
8
|
-
});
|
|
9
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
-
return target;
|
|
11
|
-
};
|
|
12
|
-
//#endregion
|
|
13
|
-
export { __exportAll };
|
package/dist/all-keyed.d.mts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//#region src/all-keyed.d.ts
|
|
2
|
-
/** The resolved form of a record passed to {@link allKeyed}. */
|
|
3
|
-
export type Settled<TRecord> = { -readonly [TKey in keyof TRecord]: Awaited<TRecord[TKey]>; };
|
|
4
|
-
/**
|
|
5
|
-
* Ponyfill of the TC39 `Promise.allKeyed` proposal: a record of promises in,
|
|
6
|
-
* a promise of the same record out, rejecting on the first failure.
|
|
7
|
-
*
|
|
8
|
-
* ```ts
|
|
9
|
-
* const data = await allKeyed({
|
|
10
|
-
* post: qc.ensure(postQuery, { slug }),
|
|
11
|
-
* author: qc.ensure(authorQuery, { slug }),
|
|
12
|
-
* });
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export declare function allKeyed<TRecord extends {
|
|
16
|
-
readonly [key: string]: unknown;
|
|
17
|
-
}>(record: TRecord): Promise<Settled<TRecord>>;
|
|
18
|
-
//#endregion
|
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 };
|