@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 +15 -1
- package/README.md +17 -17
- package/dist/client.mjs +13 -3
- package/dist/tanstack-router-browser.d.mts +11 -0
- package/dist/tanstack-router-browser.mjs +13 -0
- package/dist/tanstack-router-shared.d.mts +13 -0
- package/dist/tanstack-router-shared.mjs +48 -0
- package/dist/tanstack-router.d.mts +3 -11
- package/dist/tanstack-router.mjs +4 -30
- package/package.json +11 -3
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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 };
|
package/dist/tanstack-router.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
-
|
|
74
|
-
if (router.isServer) setupServer(options);
|
|
54
|
+
if (options.router.isServer) setupServer(options);
|
|
75
55
|
else setupClient(options);
|
|
76
|
-
|
|
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.
|
|
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":
|
|
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.
|
|
51
|
+
"@raf-query/core": "0.2.0"
|
|
44
52
|
},
|
|
45
53
|
"peerDependencies": {
|
|
46
54
|
"@tanstack/react-router": "^1.170.0",
|