Skip to content

docs(fetchium): concise docs and changeset for #75 - #76

Open
abedell-phantom wants to merge 7 commits into
fix/keep-cached-first-paintfrom
docs/concise-perf-stack
Open

abedell-phantom wants to merge 7 commits into
fix/keep-cached-first-paintfrom
docs/concise-perf-stack

Conversation

@abedell-phantom

@abedell-phantom abedell-phantom commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Rewrites the docs and changeset that #75 added. No code changes. The #67–#74 parts moved to #77.

  • stores/sync: saveEntity's rest, readEntity, the field-name methods and clear() cut to one sentence each.
  • stores/async: signatures and message types include rest and ownedKeys.
  • keep-cached-first-paint changeset: one line per change.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@abedell-phantom
abedell-phantom requested review from a team and pzuraq as code owners October 7, 2026 22:39
```

`useQuery` for components under a React `<Suspense>` boundary. It suspends only on a **cold miss**: the query has never produced a value, neither in memory nor, with a synchronous store, in the persisted cache. Anything else renders at once, including a stale value whose refetch is in flight. Refetches never suspend. Because the hook only returns once a value exists, `value` is always defined.
Like `useQuery`, but suspends while the query has no value yet. Refetches don't suspend, so `value` is always defined. A failed first fetch is thrown to the nearest error boundary, and resetting the boundary retries it.

@jimmy-phantom jimmy-phantom Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirements and examples tell users to call useQuery inside Signalium’s component(). That reaches useReactive’s reactive-context guard. These are pre-existing mistakes, but the rewrite also removes the explicit restriction from useSuspenseQuery.

This page needs to clearly distinguish hooks in ordinary React components from fetchQuery inside Signalium component(). That is essential usage guidance, not implementation detail.

Comment thread docs/src/app/api/fetchium/page.md Outdated
#### `prefetch` and `retain`

Both take a lease: the queries stay active (fetched, subscribed, exempt from GC) without a component reading them. A reader that mounts while the lease is held joins the active query, so it starts no request of its own and renders the data on its first render if it has arrived. Both return an idempotent `release` function. Releasing never interrupts other readers; once the last one goes, the query deactivates and its `gcTime` starts as usual. Calling `release` while a fetch is in flight, with no reader, aborts that fetch. A `ttl` is an upper bound: the lease goes when it runs out even if the fetch is still in flight (offline, or a topic never fulfilled), which aborts the fetch unless a reader has joined it. Call `retain` and `prefetch` from event handlers or effects, not from inside a reactive computation; called from one, they warn in development and keep only the leases from the computation's latest run.
Both keep queries active without a component reading them and return a `release` function. A component that mounts meanwhile reuses the running request. Call them from event handlers or effects, not during render.

@jimmy-phantom jimmy-phantom Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rewrite removes the explanation that releasing a hold, or reaching its TTL, can abort an unfinished request when no other reader has joined. Releasing does not interrupt readers that have joined.

Users need that distinction to choose a TTL and manage release() safely. One short sentence would suffice.

| -------------------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `loadQuery` | `(queryDef: QueryDefinition, queryKey: number): CachedQuery \| undefined` | Loads a cached query by key. Returns `undefined` if the cache entry has expired (beyond `cacheTime`) or does not exist. Also preloads referenced entities. |
| `saveQuery` | `(queryDef: QueryDefinition, queryKey: number, value: unknown, updatedAt: number, refIds?: Set<number>): void` | Persists a query result, its timestamp, and entity reference IDs. Activates the query in the LRU queue. |
| `saveEntity` | `(entityKey: number, value: unknown, refIds?: Set<number>, rest?: string): void` | Persists an entity and its child references. `rest` holds stored fields to keep that the entity's class doesn't declare. |

@jimmy-phantom jimmy-phantom Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“Stored fields to keep” doesn’t explain what this string contains. The implementation expects a JSON object body without enclosing braces, such as:

"description":"Example"

Passing a normal JSON object string produces malformed stored JSON. The signatures are updated correctly, but this input-format requirement shouldn’t be trimmed.

Comment thread .changeset/activation-timing-fixes.md Outdated
- Unmounting and remounting a query, in the same task or mid-fetch (switching a chart timeframe and back), no longer shows an `AbortError`. The query shows its cached data, or stays pending, while it refetches. One exception: a topic query with no data that mounts and unmounts in the same task shows the `AbortError` on its next mount until its data arrives (before, it stayed pending forever).
- Changing a Signal param mid-fetch fetches the new params instead of showing the old params' response. A query whose result is not an entity now caches each params value separately; a cold start could show another params value's data.
- `QueryClient.destroy()` aborts every fetch in flight, and code awaiting one gets an `AbortError`.
- Remounting a query no longer shows an `AbortError`. It shows its cached data, or stays pending, while it refetches.

@jimmy-phantom jimmy-phantom Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“Remounting a query no longer shows an AbortError” is not universally true. A cold topic query mounted and unmounted in the same task remains an exception. Its first awaiter also rejects rather than waiting for a remount.

The claims need qualification, not the previous lengthy timing explanation.

| `readEntity` | `(entityKey: number): Record<string, unknown> \| undefined` | The stored record for `entityKey`, parsed, or `undefined`. |
| `getEntityFieldNames` | `(typename: string): readonly string[] \| undefined` | Field names the classes of `typename` declared in the last 30 days. |
| `addEntityFieldNames` | `(typename: string, fields: readonly string[]): void` | Records the field names a class of `typename` declares. |
| `entityFieldNamesComplete` | `(): boolean` | Whether the remembered field names cover every stored record. `false` for 30 days after upgrading, `true` after `clear()`. |

@jimmy-phantom jimmy-phantom Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation says the field-name completeness window starts “after upgrading.” It also applies to a fresh store’s first opening.

# Conflicts:
#	docs/src/app/api/stores-async/page.md
#	docs/src/app/api/stores-sync/page.md
@abedell-phantom abedell-phantom changed the title docs(fetchium): concise docs and changesets for the perf stack docs(fetchium): concise docs and changeset for #75 Oct 8, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants