@raf-query/core 0.11.1 → 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.
Files changed (71) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +28 -18
  3. package/dist/client.d.mts +132 -121
  4. package/dist/client.mjs +59 -162
  5. package/dist/entry.d.mts +67 -148
  6. package/dist/entry.mjs +303 -474
  7. package/dist/features/focus-refetch/focus-refetch.d.mts +15 -0
  8. package/dist/features/focus-refetch/focus-refetch.mjs +18 -0
  9. package/dist/features/polling/polling.d.mts +12 -0
  10. package/dist/features/polling/polling.mjs +35 -0
  11. package/dist/features/reconnect-refetch/reconnect-refetch.d.mts +15 -0
  12. package/dist/features/reconnect-refetch/reconnect-refetch.mjs +14 -0
  13. package/dist/features/shared.mjs +13 -0
  14. package/dist/guards.mjs +23 -0
  15. package/dist/hydration/hydration.d.mts +73 -0
  16. package/dist/hydration/hydration.mjs +128 -0
  17. package/dist/index.d.mts +18 -13
  18. package/dist/index.mjs +15 -13
  19. package/dist/infinite/infinite.d.mts +71 -0
  20. package/dist/infinite/infinite.mjs +88 -0
  21. package/dist/internal.d.mts +13 -0
  22. package/dist/internal.mjs +7 -0
  23. package/dist/keys/keys.d.mts +46 -0
  24. package/dist/keys/keys.mjs +53 -0
  25. package/dist/keys/targets.d.mts +56 -0
  26. package/dist/keys/targets.mjs +85 -0
  27. package/dist/links/retry/retry.d.mts +26 -0
  28. package/dist/links/retry/retry.mjs +51 -0
  29. package/dist/links/timeout/timeout.d.mts +9 -0
  30. package/dist/links/timeout/timeout.mjs +27 -0
  31. package/dist/load.d.mts +124 -0
  32. package/dist/load.mjs +165 -0
  33. package/dist/options.d.mts +112 -351
  34. package/dist/options.mjs +25 -41
  35. package/dist/presets.d.mts +29 -0
  36. package/dist/presets.mjs +25 -0
  37. package/dist/root.d.mts +82 -0
  38. package/dist/structural-sharing/structural-sharing.mjs +32 -0
  39. package/dist/subscribe.d.mts +15 -0
  40. package/dist/subscribe.mjs +29 -0
  41. package/dist/thenable.d.mts +13 -17
  42. package/dist/thenable.mjs +25 -6
  43. package/dist/verbs.d.mts +109 -0
  44. package/dist/verbs.mjs +87 -0
  45. package/package.json +13 -9
  46. package/dist/_virtual/_rolldown/runtime.mjs +0 -13
  47. package/dist/all-keyed.d.mts +0 -18
  48. package/dist/all-keyed.mjs +0 -19
  49. package/dist/binding.d.mts +0 -92
  50. package/dist/binding.mjs +0 -186
  51. package/dist/cache.d.mts +0 -164
  52. package/dist/cache.mjs +0 -184
  53. package/dist/controls.d.mts +0 -95
  54. package/dist/controls.mjs +0 -70
  55. package/dist/hydration.d.mts +0 -84
  56. package/dist/hydration.mjs +0 -148
  57. package/dist/infinite.d.mts +0 -5
  58. package/dist/infinite.mjs +0 -103
  59. package/dist/key.d.mts +0 -18
  60. package/dist/key.mjs +0 -36
  61. package/dist/listen.mjs +0 -19
  62. package/dist/notify.mjs +0 -21
  63. package/dist/observer.d.mts +0 -110
  64. package/dist/observer.mjs +0 -300
  65. package/dist/platform.d.mts +0 -27
  66. package/dist/platform.mjs +0 -44
  67. package/dist/query.d.mts +0 -137
  68. package/dist/query.mjs +0 -147
  69. package/dist/request.mjs +0 -47
  70. package/dist/retry.mjs +0 -25
  71. package/dist/share.mjs +0 -40
@@ -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 };
@@ -1,31 +1,27 @@
1
1
  //#region src/thenable.d.ts
2
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
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
- interface PendingStatus {
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
- * A promise that says whether it has settled, and with what. `status` moves
20
- * from `"pending"` to `"fulfilled"` (with `value`) or `"rejected"` (with
21
- * `reason`) once, when the promise settles.
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 };
@@ -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.11.1",
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
- "@effect/vitest": "4.0.1",
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
- "effect": "^4.0.1",
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 };
@@ -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
@@ -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 };
@@ -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 };