@raf-query/core 0.1.0 → 0.1.1
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 +12 -6
- package/README.md +18 -8
- package/dist/cache.d.mts +13 -2
- package/dist/cache.mjs +11 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.1](https://github.com/KATT/raf-query/compare/0.1.0...0.1.1) (2026-10-12)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **core:** add predicate to QueryFilter ([#24](https://github.com/KATT/raf-query/issues/24)) ([c0b9c18](https://github.com/KATT/raf-query/commit/c0b9c1829cb3b6446f765b151e520f0195af5276))
|
|
8
|
+
|
|
3
9
|
## [0.1.0](https://github.com/KATT/raf-query/compare/0.0.19...0.1.0) (2026-10-12)
|
|
4
10
|
|
|
5
11
|
### ⚠ BREAKING CHANGES
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
- Effect-native engine; core exposes only Effect APIs ([#23](https://github.com/KATT/raf-query/issues/23))
|
|
14
|
+
- **core:** make QueryObserver an Effect-style data type ([#29](https://github.com/KATT/raf-query/issues/29))
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
- **core:** make QueryObserver an Effect-style data type ([#29](https://github.com/KATT/raf-query/issues/29)) ([50fbb45](https://github.com/KATT/raf-query/commit/50fbb45bf3edcaa83673e2177894a76bdf76560b))
|
|
17
|
+
- Effect-native engine; core exposes only Effect APIs ([#23](https://github.com/KATT/raf-query/issues/23)) ([8e0bb09](https://github.com/KATT/raf-query/commit/8e0bb09a63d644ede053f443406a521ff2613a6f))
|
|
12
18
|
|
|
13
19
|
### Features
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
- **core:** stream SSR dehydration as Effect APIs; integrations become thin edges ([#30](https://github.com/KATT/raf-query/issues/30)) ([6b2b570](https://github.com/KATT/raf-query/commit/6b2b5703f5463c0f3805c233cf4ead90df9efb76)), references [#29](https://github.com/KATT/raf-query/issues/29)
|
|
22
|
+
- typed services for Effect queries; QueryClient layer and dispose() ([#31](https://github.com/KATT/raf-query/issues/31)) ([6aeaa3d](https://github.com/KATT/raf-query/commit/6aeaa3d356f6483654729620ac26762508eb8fd8))
|
|
17
23
|
|
|
18
24
|
## [0.0.19](https://github.com/KATT/raf-query/compare/0.0.18...0.0.19) (2026-10-12)
|
|
19
25
|
|
package/README.md
CHANGED
|
@@ -358,12 +358,22 @@ function EditTitle(props: { post: Post }) {
|
|
|
358
358
|
|
|
359
359
|
`updatePost` is a server function (or a Next.js server action). `await qc.invalidate(...)` resolves once every query currently being read has refetched, so the action's pending state lasts until the fresh data is on screen. For an error message, wrap the action in `useActionState` and return the caught error.
|
|
360
360
|
|
|
361
|
-
| Call | Effect
|
|
362
|
-
| ------------------------------------- |
|
|
363
|
-
| `qc.invalidate(factory, input)` | Mark one query stale and refetch it if it is being read
|
|
364
|
-
| `qc.invalidate(
|
|
365
|
-
| `qc.setData(factory, input, updater)` | Write data directly
|
|
366
|
-
| `qc.getData(factory, input)` | Read cached data, or `undefined`
|
|
361
|
+
| Call | Effect |
|
|
362
|
+
| ------------------------------------- | ------------------------------------------------------- |
|
|
363
|
+
| `qc.invalidate(factory, input)` | Mark one query stale and refetch it if it is being read |
|
|
364
|
+
| `qc.invalidate(filter)` | Same, for every query the [filter](#filters) selects |
|
|
365
|
+
| `qc.setData(factory, input, updater)` | Write data directly |
|
|
366
|
+
| `qc.getData(factory, input)` | Read cached data, or `undefined` |
|
|
367
|
+
|
|
368
|
+
### Filters
|
|
369
|
+
|
|
370
|
+
`invalidate`, `reset` and `remove` also take a filter, `{ queryKey?, exact?, predicate? }`. `queryKey` selects every query whose key starts with it, or only that key with `exact: true`; object segments match partially, so `{ queryKey: ["issues", { projectId: 3 }] }` selects every issue list in project 3. `predicate` gets each remaining `Query` and keeps the ones it returns `true` for: read its `key` and `handle.input`, or use the `Query` functions (`Query.getUnsafe(query)` for its state, `Query.observerCountUnsafe(query)`, `Query.isStaleUnsafe(query)`). Without `queryKey` every key matches, so `{}` selects the whole cache.
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
qc.remove({ predicate: (query) => Query.observerCountUnsafe(query) === 0 }); // everything not being read
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
`handle.input` is the input of the latest factory call for that entry. An entry that was only hydrated, and not yet read or started on this client, has `undefined` input, so match on `key` to include those.
|
|
367
377
|
|
|
368
378
|
Data can be `undefined` when the `queryFn`'s type allows it (`Promise<User | undefined>`); it's cached and reused like any other value, unlike in TanStack Query, where an `undefined` result is an error. So `getData` and an updater's `previous` return `undefined` both with no data and with `undefined` data; `Query.getUnsafe(qc.getQuery(factory, input)).hasData` tells them apart.
|
|
369
379
|
|
|
@@ -598,8 +608,8 @@ Both render nothing on the server, since the server's cache isn't the browser's.
|
|
|
598
608
|
| `new QueryClient(config?)` | `config.defaults` (query options below), `config.onError`, `config.isServer`, `config.layer` ([services](#services)) |
|
|
599
609
|
| `prefetch`, `ensure`, `fetch` | [Start queries](#starting-queries) |
|
|
600
610
|
| `invalidate`, `setData`, `getData` | [Write and read the cache](#mutations) |
|
|
601
|
-
| `reset(factory, input)` / `reset(filter)` | Drop data and errors; read queries suspend and refetch. Resolves when they settle
|
|
602
|
-
| `remove(factory, input)` / `remove(filter)` | Remove entries and abort their requests
|
|
611
|
+
| `reset(factory, input)` / `reset(filter)` | Drop data and errors; read queries suspend and refetch. Resolves when they settle. [Filters](#filters) |
|
|
612
|
+
| `remove(factory, input)` / `remove(filter)` | Remove entries and abort their requests. [Filters](#filters) |
|
|
603
613
|
| `effect` | The [Effect](#effect) `QueryCache` behind the client |
|
|
604
614
|
| `subscribe(listener)` | Cache events: `added`, `updated`, `observers` (a reader subscribed or left), `removed`, and `failed` (with the `error`) |
|
|
605
615
|
| `getAll()`, `getQuery(factory, input?)` | Cache entries, for devtools and integrations |
|
package/dist/cache.d.mts
CHANGED
|
@@ -30,10 +30,21 @@ export type Event = {
|
|
|
30
30
|
readonly query: Query;
|
|
31
31
|
readonly type: "failed";
|
|
32
32
|
};
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Selects cache entries: by key prefix (exact key with `exact`), then by
|
|
35
|
+
* `predicate`. Without `queryKey` every key matches, so `{}` selects the
|
|
36
|
+
* whole cache.
|
|
37
|
+
*/
|
|
34
38
|
export interface Filter {
|
|
35
39
|
readonly exact?: boolean;
|
|
36
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Keep only entries this returns `true` for; runs after key matching.
|
|
42
|
+
* `query.handle.input` is the input of the latest factory call, and stays
|
|
43
|
+
* `undefined` for an entry only hydrated so far; match on `query.key` to
|
|
44
|
+
* include those.
|
|
45
|
+
*/
|
|
46
|
+
readonly predicate?: (query: Query) => boolean;
|
|
47
|
+
readonly queryKey?: QueryKey;
|
|
37
48
|
}
|
|
38
49
|
/** `ensure` / `fetch` arguments: the input, then load options for paged factories. */
|
|
39
50
|
export type LoadArgs<TFactory extends QueryFactory> = InputArgs<TFactory> | [input: InputOf<TFactory>, options: LoadOptions<TFactory>];
|
package/dist/cache.mjs
CHANGED
|
@@ -44,6 +44,15 @@ function handleOf(entry) {
|
|
|
44
44
|
function dataOf(entry) {
|
|
45
45
|
return unsafeCast(entry.state.data);
|
|
46
46
|
}
|
|
47
|
+
function keyMatcher(filter) {
|
|
48
|
+
const prefix = filter.queryKey;
|
|
49
|
+
if (prefix === void 0) return () => true;
|
|
50
|
+
if (filter.exact) {
|
|
51
|
+
const hash = hashKey(prefix);
|
|
52
|
+
return (entry) => entry.hash === hash;
|
|
53
|
+
}
|
|
54
|
+
return (entry) => matchesKey(entry.key, prefix);
|
|
55
|
+
}
|
|
47
56
|
/**
|
|
48
57
|
* Background refetch of cached data a verb reuses. A server cache renders
|
|
49
58
|
* once, so a refetch there could only duplicate a request whose result is
|
|
@@ -113,8 +122,8 @@ const make = Effect.fnUntraced(function* (config = {}) {
|
|
|
113
122
|
const entry = state.entries.get(hashKey(optionsFor(first, input).queryKey));
|
|
114
123
|
return entry ? [entry] : [];
|
|
115
124
|
}
|
|
116
|
-
const
|
|
117
|
-
return [...state.entries.values()].filter((entry) =>
|
|
125
|
+
const keyMatches = keyMatcher(first);
|
|
126
|
+
return [...state.entries.values()].filter((entry) => keyMatches(entry) && (first.predicate?.(entry) ?? true));
|
|
118
127
|
};
|
|
119
128
|
const subscription = yield* RcRef.make({ acquire: Effect.acquireRelease(Effect.sync(() => platform.subscribeUnsafe((event) => {
|
|
120
129
|
for (const entry of state.entries.values()) if (isObserved(entry)) onPlatformEvent(entry, event);
|