Skip to content

Commit 05f994a

Browse files
committed
chore: rename and update agent skill from studiolambda-query to lambda-query
1 parent 6a3f6a1 commit 05f994a

File tree

1 file changed

+29
-17
lines changed

1 file changed

+29
-17
lines changed
Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
---
2-
name: studiolambda-query
2+
name: lambda-query
33
description: >
44
Skill for using @studiolambda/query, a lightweight isomorphic SWR-style async data
55
management library. Use when writing, editing, reviewing, or testing code that uses
66
createQuery, useQuery, QueryProvider, cache mutations, hydration, event subscriptions,
77
or any @studiolambda/query API. Covers the core framework-agnostic library and
88
React 19+ bindings (hooks and components).
9-
license: MIT
10-
compatibility: Requires Node.js 18+. React bindings require React 19.1+.
119
metadata:
12-
version: "1.4.0"
10+
version: "1.5.9"
1311
author: studiolambda
1412
---
1513

@@ -96,6 +94,8 @@ await query.forget(/^\/api\/users(.*)/) // Regex pattern
9694
await query.forget() // All keys
9795
```
9896

97+
**Note:** `forget` only removes items from the items cache -- it does not cancel pending resolvers. Use `abort` to cancel in-flight requests. If a cached promise has rejected, `forget` handles it gracefully (emits `'forgotten'` with `undefined`).
98+
9999
### Hydrate (pre-populate cache)
100100

101101
```typescript
@@ -112,12 +112,14 @@ query.abort() // Abort all pending
112112
query.abort('/api/user', 'cancelled') // With custom reason
113113
```
114114

115+
**Note:** When `fresh: true` is used, `abort(key)` is called before `refetch(key)` to ensure a genuinely new fetch starts instead of returning the pending deduplication promise.
116+
115117
### Inspect cache
116118

117119
```typescript
118120
const value = await query.snapshot<User>('/api/user') // Current cached value or undefined
119-
const itemKeys = query.keys('items') // Cached item keys
120-
const resolverKeys = query.keys('resolvers') // Pending resolver keys
121+
const itemKeys = query.keys('items') // readonly string[]
122+
const resolverKeys = query.keys('resolvers') // readonly string[]
121123
const date = query.expiration('/api/user') // Expiration Date or undefined
122124
```
123125

@@ -138,19 +140,21 @@ const unsub = query.subscribe('/api/user', 'resolved', (event) => {
138140
})
139141
unsub()
140142

141-
// One-time listener
143+
// One-time listener (supports optional AbortSignal for cleanup)
142144
const event = await query.once('/api/user', 'resolved')
145+
const event = await query.once('/api/user', 'resolved', signal) // cancellable
143146

144-
// Await next fetch resolution
147+
// Await next fetch resolution (supports optional AbortSignal)
145148
const result = await query.next<string>('/api/user')
146149
const [a, b] = await query.next<[User, Config]>(['/api/user', '/api/config'])
150+
const obj = await query.next<{ user: User }>({ user: '/api/user' })
147151

148-
// Stream resolutions (async generator)
152+
// Stream resolutions (async generator -- cleans up listeners on break/return)
149153
for await (const value of query.stream<User>('/api/user')) {
150154
console.log(value)
151155
}
152156

153-
// Stream arbitrary events (async generator)
157+
// Stream arbitrary events (async generator -- cleans up listeners on break/return)
154158
for await (const event of query.sequence<User>('/api/user', 'resolved')) {
155159
console.log(event.detail)
156160
}
@@ -166,9 +170,11 @@ const unsub = query.subscribeBroadcast()
166170
unsub()
167171
```
168172

173+
**Note:** `subscribeBroadcast()` captures the broadcast reference at call time. If `configure()` later replaces the channel, the unsubscriber still targets the original. `emit()` wraps `postMessage` in try-catch for non-cloneable data.
174+
169175
## React Bindings
170176

171-
Designed for React 19+ with first-class Suspense and Transitions support.
177+
Designed for React 19+ with first-class Suspense and Transitions support. Uses React Compiler for automatic memoization -- do NOT use `useMemo`, `useCallback`, or `React.memo`.
172178

173179
### Setup
174180

@@ -194,7 +200,7 @@ function App() {
194200
- `clearOnForget?` - Auto-refetch after `forget()` (default `false`)
195201
- `ignoreTransitionContext?` - Use local transitions instead of shared (default `false`)
196202

197-
`QueryProvider` automatically handles BroadcastChannel setup and cleanup.
203+
`QueryProvider` automatically handles BroadcastChannel setup, cleanup, and cross-tab event forwarding. Includes a guard for environments where `BroadcastChannel` is unavailable.
198204

199205
### useQuery
200206

@@ -251,7 +257,7 @@ const { expiresAt, isExpired, isRefetching, isMutating } = useQueryStatus('/api/
251257

252258
### useQueryBasic
253259

254-
Minimal hook returning only `data` and `isPending`.
260+
Minimal hook returning only `data` and `isPending`. Correctly resets data when the key changes to a different cached value.
255261

256262
```tsx
257263
const { data, isPending } = useQueryBasic<User>('/api/user/1')
@@ -348,12 +354,18 @@ Pattern: create query with mock fetcher, pass it via `{ query }` option to bypas
348354

349355
- **Expiration is a function, not a number.** Always `expiration: () => 5000`, not `expiration: 5000`.
350356
- **`useQuery` suspends.** Components must be inside `<Suspense>` or React throws.
351-
- **`data` from `useQuery` is always resolved.** It's never undefined or null from a loading state. Suspense handles loading.
357+
- **`data` from `useQuery` is always resolved.** Never undefined/null from loading state. Suspense handles loading.
352358
- **`hydrate` without expiration creates immediately-stale data.** The first `query()` returns the hydrated value, the second triggers a refetch.
353-
- **Mutation with `expiration: () => 0` makes the value immediately stale.** Provide a non-zero expiration in `mutate` options if you want it to persist.
354-
- **`forget` does not cancel pending fetches.** Use `abort` to cancel in-flight requests.
359+
- **Mutation with `expiration: () => 0` makes the value immediately stale.** Provide a non-zero expiration if you want it to persist.
360+
- **`forget` does not cancel pending fetches.** Only removes items from the items cache. Use `abort` to cancel in-flight requests.
355361
- **`stale: false` blocks until refetch completes.** Default `stale: true` returns old data while revalidating in the background.
356-
- **`subscribe('refetching')` on a key with a pending resolver fires immediately.** This is intentional for late subscribers.
362+
- **`subscribe('refetching')` on a key with a pending resolver fires immediately.** Intentional for late subscribers.
357363
- **BroadcastChannel is not auto-created in vanilla usage.** `QueryProvider` handles it in React. In core, configure it manually.
358364
- **Pass stable `keys` arrays to `useQueryPrefetch` / `QueryPrefetch`.** Use `useMemo` or a module-level constant to avoid infinite re-renders.
359365
- **`useQueryInstance` throws if no query is in context or options.** Ensure `QueryProvider` is an ancestor or pass `{ query }` in options.
366+
- **React Compiler handles memoization.** Do NOT use `useMemo`, `useCallback`, or `React.memo` -- the compiler does it automatically.
367+
- **`once()` and `next()` accept an optional `AbortSignal`.** Use to cancel pending listeners when breaking out of generators.
368+
- **`stream()` and `sequence()` clean up on break.** Internal `AbortController` cancels pending listeners via `finally` block.
369+
- **Abort race condition is handled.** If `abort()` fires after fetch resolves but before cache write, the result is discarded and the promise rejects.
370+
- **`next()` supports object keys.** `await query.next<{ user: User }>({ user: '/api/user' })` returns an object with the same shape.
371+
- **`fresh: true` aborts then refetches.** Ensures a genuinely new fetch instead of returning the pending deduplication promise.

0 commit comments

Comments
 (0)