@raf-query/react 0.1.16 → 0.2.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 CHANGED
@@ -1,10 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0](https://github.com/KATT/raf-query/compare/0.1.16...0.2.0) (2026-10-13)
4
+
5
+ ### ⚠ BREAKING CHANGES
6
+
7
+ * **react:** qc.fetch / qc.ensure reject with an AbortError when cancelled ([#47](https://github.com/KATT/raf-query/issues/47))
8
+
9
+ ### Bug Fixes
10
+
11
+ * **react:** qc.fetch / qc.ensure reject with an AbortError when cancelled ([#47](https://github.com/KATT/raf-query/issues/47)) ([53aa90b](https://github.com/KATT/raf-query/commit/53aa90b4b6068cff3998b189d3d563f338a3e564))
12
+
13
+ ### Performance Improvements
14
+
15
+ * **react:** browser build of the TanStack Router integration ([#48](https://github.com/KATT/raf-query/issues/48)) ([75e78cf](https://github.com/KATT/raf-query/commit/75e78cf968c069188a2688ad3298fb4bee2e3b8c))
16
+
3
17
  ## [0.1.16](https://github.com/KATT/raf-query/compare/0.1.15...0.1.16) (2026-10-13)
4
18
 
5
19
  ### Bug Fixes
6
20
 
7
- * stale data always refreshes when switching pages ([#49](https://github.com/KATT/raf-query/issues/49)) ([68652cd](https://github.com/KATT/raf-query/commit/68652cdf6ea06ef86d277329098ee294dba98f21))
21
+ - stale data always refreshes when switching pages ([#49](https://github.com/KATT/raf-query/issues/49)) ([68652cd](https://github.com/KATT/raf-query/commit/68652cdf6ea06ef86d277329098ee294dba98f21))
8
22
 
9
23
  ## [0.1.15](https://github.com/KATT/raf-query/compare/0.1.14...0.1.15) (2026-10-13)
10
24
 
package/README.md CHANGED
@@ -751,23 +751,23 @@ When the last reader of a query unsubscribes during a background refetch, a `que
751
751
 
752
752
  `@raf-query/core`:
753
753
 
754
- | Export | Use |
755
- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
756
- | `QueryCache` | The cache service (`qc.effect`): `make`, `layer`, and the verbs as Effects |
757
- | `Query` | Cache entries: `Query.fetch`, `Query.changes`, `Query.getUnsafe`, `Query.metaUnsafe`, … |
758
- | `Platform` | The page the cache watches (focus, network); swap it in tests |
759
- | `queryOptions(options)` | Typed options for a query factory |
760
- | `infiniteQueryOptions(options)` | Typed options for a paged query factory |
761
- | `hashKey(key)`, `matchesKey(key, prefix)` | Key helpers |
762
- | `QueryObserver` | One reader's view of a query, with tracked results; for bindings |
763
- | `Query.CancelledError` | Failure of a request whose entry was removed or cleared before it settled |
764
- | `RedactedError` | What a streamed pending query rejects with when its server request failed |
765
- | `QueryHandle<TFactory>` | Handle type for props |
766
- | `EnsuredQueryHandle<TFactory>` | Handle type for props fed by `ensure` |
767
- | `Thenable<T>` | Promise with `status` / `value` / `reason`, read by `use()` |
768
- | `DataOf<TFactory>`, `InputOf<TFactory>`, `ErrorOf<TFactory>` | A factory's data, input and error types |
769
- | `ServicesOf<TFactory>` | The services a factory's `queryFn` Effect needs; `never` for promises |
770
- | `Register`, `QueryMeta` | Augment `Register` with `queryMeta` to type [`meta`](#query-meta) |
754
+ | Export | Use |
755
+ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
756
+ | `QueryCache` | The cache service (`qc.effect`): `make`, `layer`, and the verbs as Effects |
757
+ | `Query` | Cache entries: `Query.fetch`, `Query.changes`, `Query.getUnsafe`, `Query.metaUnsafe`, … |
758
+ | `Platform` | The page the cache watches (focus, network); swap it in tests |
759
+ | `queryOptions(options)` | Typed options for a query factory |
760
+ | `infiniteQueryOptions(options)` | Typed options for a paged query factory |
761
+ | `hashKey(key)`, `matchesKey(key, prefix)` | Key helpers |
762
+ | `QueryObserver` | One reader's view of a query, with tracked results; for bindings |
763
+ | `Query.CancelledError` | Failure of an Effect request whose entry was removed or cleared before it settled. `qc.fetch` / `qc.ensure` reject with an `AbortError` `DOMException` instead, as an aborted `fetch` does |
764
+ | `RedactedError` | What a streamed pending query rejects with when its server request failed |
765
+ | `QueryHandle<TFactory>` | Handle type for props |
766
+ | `EnsuredQueryHandle<TFactory>` | Handle type for props fed by `ensure` |
767
+ | `Thenable<T>` | Promise with `status` / `value` / `reason`, read by `use()` |
768
+ | `DataOf<TFactory>`, `InputOf<TFactory>`, `ErrorOf<TFactory>` | A factory's data, input and error types |
769
+ | `ServicesOf<TFactory>` | The services a factory's `queryFn` Effect needs; `never` for promises |
770
+ | `Register`, `QueryMeta` | Augment `Register` with `queryMeta` to type [`meta`](#query-meta) |
771
771
 
772
772
  `@raf-query/react` (plus all of the above):
773
773
 
package/dist/client.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { unsafeCast } from "./cast.mjs";
2
2
  import { QueryCache } from "@raf-query/core";
3
- import { Context, Effect, Exit, Layer, Option, Scope } from "effect";
3
+ import { Context, Effect, Exit, Layer, Option, Predicate, Scope } from "effect";
4
4
  //#region src/client.ts
5
5
  /**
6
6
  * A verb's Effect, run by a client. Its requirement is the factory's
@@ -11,6 +11,16 @@ function provided(effect) {
11
11
  return unsafeCast(effect);
12
12
  }
13
13
  /**
14
+ * Run a verb that waits on a request. A cancelled request rejects with an
15
+ * `AbortError`, as an aborted `fetch` does, not with the Effect API's
16
+ * `Query.CancelledError`.
17
+ */
18
+ function runLoad(effect) {
19
+ return Effect.runPromise(effect).catch((error) => {
20
+ throw Predicate.isTagged(error, "CancelledError") ? new DOMException("The query was cancelled", "AbortError") : error;
21
+ });
22
+ }
23
+ /**
14
24
  * The query cache, with Promise methods. Create one per request on the
15
25
  * server and one per browser tab. `effect` is the same cache's Effect API.
16
26
  * Its verbs take factories whose `queryFn` needs at most `TServices`, the
@@ -57,11 +67,11 @@ var QueryClient = class {
57
67
  * directly.
58
68
  */
59
69
  ensure(factory, ...args) {
60
- return Effect.runPromise(provided(this.effect.ensure(factory, ...args)));
70
+ return runLoad(provided(this.effect.ensure(factory, ...args)));
61
71
  }
62
72
  /** Like `ensure`, but resolves to the data. */
63
73
  fetch(factory, ...args) {
64
- return Effect.runPromise(provided(this.effect.fetch(factory, ...args)));
74
+ return runLoad(provided(this.effect.fetch(factory, ...args)));
65
75
  }
66
76
  /**
67
77
  * Mark queries stale and refetch the ones being read. Resolves once those
@@ -0,0 +1,11 @@
1
+ import { RouterIntegrationOptions } from "./tanstack-router-shared.mjs";
2
+ import { AnyRouter } from "@tanstack/react-router";
3
+ //#region src/tanstack-router-browser.d.ts
4
+ /**
5
+ * The browser build of `@raf-query/react/tanstack-router` (the `browser`
6
+ * export condition): the client half only, so the server's dehydration
7
+ * stream stays out of the app bundle.
8
+ */
9
+ export declare function setupRouterIntegration<TRouter extends AnyRouter>(options: RouterIntegrationOptions<TRouter>): void;
10
+ //#endregion
11
+ export type { RouterIntegrationOptions };
@@ -0,0 +1,13 @@
1
+ import { handleRedirects, setupClient } from "./tanstack-router-shared.mjs";
2
+ //#region src/tanstack-router-browser.ts
3
+ /**
4
+ * The browser build of `@raf-query/react/tanstack-router` (the `browser`
5
+ * export condition): the client half only, so the server's dehydration
6
+ * stream stays out of the app bundle.
7
+ */
8
+ function setupRouterIntegration(options) {
9
+ setupClient(options);
10
+ handleRedirects(options);
11
+ }
12
+ //#endregion
13
+ export { setupRouterIntegration };
@@ -0,0 +1,13 @@
1
+ import { QueryClient } from "./client.mjs";
2
+ import { DehydrateOptions, DehydratedState, HydrateOptions } from "@raf-query/core";
3
+ import { AnyRouter } from "@tanstack/react-router";
4
+ //#region src/tanstack-router-shared.d.ts
5
+ export interface RouterIntegrationOptions<TRouter extends AnyRouter> {
6
+ readonly dehydrateOptions?: DehydrateOptions;
7
+ /** Navigate when a `queryFn` throws `redirect()`. Default `true`. */
8
+ readonly handleRedirects?: boolean;
9
+ readonly hydrateOptions?: HydrateOptions;
10
+ readonly queryClient: QueryClient;
11
+ readonly router: TRouter;
12
+ }
13
+ //#endregion
@@ -0,0 +1,48 @@
1
+ import { hydrate } from "./client.mjs";
2
+ import { Predicate } from "effect";
3
+ import { isRedirect } from "@tanstack/react-router";
4
+ //#region src/tanstack-router-shared.ts
5
+ function hasRafQueries(value) {
6
+ return Predicate.hasProperty(value, "raf") && Predicate.isObject(value.raf);
7
+ }
8
+ /**
9
+ * Hydrate each state as the server's stream delivers it. Reads the stream
10
+ * directly: `Stream.fromReadableStream` + `QueryCache.hydrateStream` would
11
+ * put Effect's `Stream` and `Queue` in the browser bundle.
12
+ */
13
+ async function hydrateEach(client, stream, options) {
14
+ const reader = stream.getReader();
15
+ for (;;) {
16
+ const next = await reader.read();
17
+ if (next.done) return;
18
+ hydrate(client, next.value, options);
19
+ }
20
+ }
21
+ function setupClient(options) {
22
+ const router = options.router;
23
+ const client = options.queryClient;
24
+ const hydrateOptions = options.hydrateOptions;
25
+ const originalHydrate = router.options.hydrate;
26
+ router.options.hydrate = async (dehydrated) => {
27
+ await originalHydrate?.(dehydrated);
28
+ if (!hasRafQueries(dehydrated)) return;
29
+ const raf = dehydrated.raf;
30
+ hydrate(client, raf.initial, hydrateOptions);
31
+ hydrateEach(client, raf.stream, hydrateOptions).catch((error) => {
32
+ console.error("raf-query: reading the query stream failed", error);
33
+ });
34
+ };
35
+ }
36
+ function handleRedirects(options) {
37
+ if (!(options.handleRedirects ?? true)) return;
38
+ const router = options.router;
39
+ options.queryClient.subscribe((event) => {
40
+ if (event.type === "failed" && isRedirect(event.error)) {
41
+ const redirect = event.error;
42
+ redirect.options._fromLocation = router.stores.location.get();
43
+ router.navigate(router.resolveRedirect(redirect).options);
44
+ }
45
+ });
46
+ }
47
+ //#endregion
48
+ export { handleRedirects, setupClient };
@@ -1,15 +1,6 @@
1
- import { QueryClient } from "./client.mjs";
2
- import { DehydrateOptions, HydrateOptions } from "@raf-query/core";
1
+ import { RouterIntegrationOptions } from "./tanstack-router-shared.mjs";
3
2
  import { AnyRouter } from "@tanstack/react-router";
4
3
  //#region src/tanstack-router.d.ts
5
- export interface RouterIntegrationOptions<TRouter extends AnyRouter> {
6
- readonly dehydrateOptions?: DehydrateOptions;
7
- /** Navigate when a `queryFn` throws `redirect()`. Default `true`. */
8
- readonly handleRedirects?: boolean;
9
- readonly hydrateOptions?: HydrateOptions;
10
- readonly queryClient: QueryClient;
11
- readonly router: TRouter;
12
- }
13
4
  /**
14
5
  * Connects a query client to a TanStack Router (and TanStack Start) router:
15
6
  *
@@ -22,4 +13,5 @@ export interface RouterIntegrationOptions<TRouter extends AnyRouter> {
22
13
  * Call it in `getRouter`, which runs once per request on the server.
23
14
  */
24
15
  export declare function setupRouterIntegration<TRouter extends AnyRouter>(options: RouterIntegrationOptions<TRouter>): void;
25
- //#endregion
16
+ //#endregion
17
+ export type { RouterIntegrationOptions };
@@ -1,5 +1,5 @@
1
- import { Effect, Exit, Predicate, Scope, Stream } from "effect";
2
- import { isRedirect } from "@tanstack/react-router";
1
+ import { handleRedirects, setupClient } from "./tanstack-router-shared.mjs";
2
+ import { Effect, Exit, Scope, Stream } from "effect";
3
3
  //#region src/tanstack-router.ts
4
4
  const noop = () => {};
5
5
  function setupServer(options) {
@@ -39,25 +39,6 @@ function setupServer(options) {
39
39
  };
40
40
  };
41
41
  }
42
- function hasRafQueries(value) {
43
- return Predicate.hasProperty(value, "raf") && Predicate.isObject(value.raf);
44
- }
45
- function setupClient(options) {
46
- const router = options.router;
47
- const cache = options.queryClient.effect;
48
- const hydrateOptions = options.hydrateOptions;
49
- const originalHydrate = router.options.hydrate;
50
- router.options.hydrate = async (dehydrated) => {
51
- await originalHydrate?.(dehydrated);
52
- if (!hasRafQueries(dehydrated)) return;
53
- const raf = dehydrated.raf;
54
- Effect.runSync(cache.hydrate(raf.initial, hydrateOptions));
55
- Effect.runFork(cache.hydrateStream(Stream.fromReadableStream({
56
- evaluate: () => raf.stream,
57
- onError: (error) => error
58
- }), hydrateOptions).pipe(Effect.catchCause((cause) => Effect.logError("raf-query: reading the query stream failed", cause))));
59
- };
60
- }
61
42
  /**
62
43
  * Connects a query client to a TanStack Router (and TanStack Start) router:
63
44
  *
@@ -70,16 +51,9 @@ function setupClient(options) {
70
51
  * Call it in `getRouter`, which runs once per request on the server.
71
52
  */
72
53
  function setupRouterIntegration(options) {
73
- const router = options.router;
74
- if (router.isServer) setupServer(options);
54
+ if (options.router.isServer) setupServer(options);
75
55
  else setupClient(options);
76
- if (options.handleRedirects ?? true) options.queryClient.subscribe((event) => {
77
- if (event.type === "failed" && isRedirect(event.error)) {
78
- const redirect = event.error;
79
- redirect.options._fromLocation = router.stores.location.get();
80
- router.navigate(router.resolveRedirect(redirect).options);
81
- }
82
- });
56
+ handleRedirects(options);
83
57
  }
84
58
  //#endregion
85
59
  export { setupRouterIntegration };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raf-query/react",
3
- "version": "0.1.16",
3
+ "version": "0.2.0",
4
4
  "description": "React bindings for raf-query, a render-as-you-fetch query library",
5
5
  "keywords": [
6
6
  "query",
@@ -37,10 +37,18 @@
37
37
  "./devtools/production": "./dist/devtools-production.mjs",
38
38
  "./next": "./dist/next.mjs",
39
39
  "./rsc": "./dist/rsc.mjs",
40
- "./tanstack-router": "./dist/tanstack-router.mjs"
40
+ "./tanstack-router": {
41
+ "workerd": "./dist/tanstack-router.mjs",
42
+ "worker": "./dist/tanstack-router.mjs",
43
+ "node": "./dist/tanstack-router.mjs",
44
+ "deno": "./dist/tanstack-router.mjs",
45
+ "bun": "./dist/tanstack-router.mjs",
46
+ "browser": "./dist/tanstack-router-browser.mjs",
47
+ "default": "./dist/tanstack-router.mjs"
48
+ }
41
49
  },
42
50
  "dependencies": {
43
- "@raf-query/core": "0.1.16"
51
+ "@raf-query/core": "0.2.0"
44
52
  },
45
53
  "peerDependencies": {
46
54
  "@tanstack/react-router": "^1.170.0",