fetchium 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.
Files changed (138) hide show
  1. package/CHANGELOG.md +6 -5
  2. package/dist/cjs/development/QueryClient-CLi3ONNM.js +2 -0
  3. package/dist/cjs/development/QueryClient-CLi3ONNM.js.map +1 -0
  4. package/dist/cjs/development/QueryController-BQA49OYU.js +2 -0
  5. package/dist/cjs/development/QueryController-BQA49OYU.js.map +1 -0
  6. package/dist/cjs/development/index.js +1 -1
  7. package/dist/cjs/development/index.js.map +1 -1
  8. package/dist/cjs/development/mutation-CikIl_6k.js +2 -0
  9. package/dist/cjs/development/mutation-CikIl_6k.js.map +1 -0
  10. package/dist/cjs/development/react/index.js +1 -1
  11. package/dist/cjs/development/rest/index.js +2 -0
  12. package/dist/cjs/development/rest/index.js.map +1 -0
  13. package/dist/cjs/development/topic/index.js +2 -0
  14. package/dist/cjs/development/topic/index.js.map +1 -0
  15. package/dist/cjs/production/QueryClient-N0MJmuHW.js +2 -0
  16. package/dist/cjs/production/QueryClient-N0MJmuHW.js.map +1 -0
  17. package/dist/cjs/production/QueryController-BQA49OYU.js +2 -0
  18. package/dist/cjs/production/QueryController-BQA49OYU.js.map +1 -0
  19. package/dist/cjs/production/index.js +1 -1
  20. package/dist/cjs/production/index.js.map +1 -1
  21. package/dist/cjs/production/mutation-P_Yb4LI9.js +2 -0
  22. package/dist/cjs/production/mutation-P_Yb4LI9.js.map +1 -0
  23. package/dist/cjs/production/react/index.js +1 -1
  24. package/dist/cjs/production/rest/index.js +2 -0
  25. package/dist/cjs/production/rest/index.js.map +1 -0
  26. package/dist/cjs/production/topic/index.js +2 -0
  27. package/dist/cjs/production/topic/index.js.map +1 -0
  28. package/dist/esm/MutationResult.d.ts +0 -1
  29. package/dist/esm/MutationResult.d.ts.map +1 -1
  30. package/dist/esm/QueryClient.d.ts +26 -4
  31. package/dist/esm/QueryClient.d.ts.map +1 -1
  32. package/dist/esm/QueryController.d.ts +49 -0
  33. package/dist/esm/QueryController.d.ts.map +1 -0
  34. package/dist/esm/QueryResult.d.ts +10 -10
  35. package/dist/esm/QueryResult.d.ts.map +1 -1
  36. package/dist/esm/development/QueryClient-Dtde3pss.js +2572 -0
  37. package/dist/esm/development/QueryClient-Dtde3pss.js.map +1 -0
  38. package/dist/esm/development/QueryController-Ch_ncxiI.js +14 -0
  39. package/dist/esm/development/QueryController-Ch_ncxiI.js.map +1 -0
  40. package/dist/esm/development/index.js +29 -100
  41. package/dist/esm/development/index.js.map +1 -1
  42. package/dist/esm/development/mutation-UZshUQAf.js +58 -0
  43. package/dist/esm/development/mutation-UZshUQAf.js.map +1 -0
  44. package/dist/esm/development/react/index.js +1 -1
  45. package/dist/esm/development/rest/index.js +142 -0
  46. package/dist/esm/development/rest/index.js.map +1 -0
  47. package/dist/esm/development/{shared-Dq2yW78d.js → shared-DcuVH8Pf.js} +5 -5
  48. package/dist/esm/development/{shared-Dq2yW78d.js.map → shared-DcuVH8Pf.js.map} +1 -1
  49. package/dist/esm/development/stores/async.js +6 -6
  50. package/dist/esm/development/stores/sync.js +5 -5
  51. package/dist/esm/development/topic/index.js +86 -0
  52. package/dist/esm/development/topic/index.js.map +1 -0
  53. package/dist/esm/index.d.ts +5 -4
  54. package/dist/esm/index.d.ts.map +1 -1
  55. package/dist/esm/mutation.d.ts +6 -19
  56. package/dist/esm/mutation.d.ts.map +1 -1
  57. package/dist/esm/production/{QueryClient-BP0Z1rQV.js → QueryClient-YqnBxFy1.js} +972 -968
  58. package/dist/esm/production/QueryClient-YqnBxFy1.js.map +1 -0
  59. package/dist/esm/production/QueryController-Ch_ncxiI.js +14 -0
  60. package/dist/esm/production/QueryController-Ch_ncxiI.js.map +1 -0
  61. package/dist/esm/production/index.js +29 -100
  62. package/dist/esm/production/index.js.map +1 -1
  63. package/dist/esm/production/mutation-pgFl1uIY.js +58 -0
  64. package/dist/esm/production/mutation-pgFl1uIY.js.map +1 -0
  65. package/dist/esm/production/react/index.js +1 -1
  66. package/dist/esm/production/rest/index.js +142 -0
  67. package/dist/esm/production/rest/index.js.map +1 -0
  68. package/dist/esm/production/{shared-Dq2yW78d.js → shared-DcuVH8Pf.js} +5 -5
  69. package/dist/esm/production/{shared-Dq2yW78d.js.map → shared-DcuVH8Pf.js.map} +1 -1
  70. package/dist/esm/production/stores/async.js +6 -6
  71. package/dist/esm/production/stores/sync.js +5 -5
  72. package/dist/esm/production/topic/index.js +86 -0
  73. package/dist/esm/production/topic/index.js.map +1 -0
  74. package/dist/esm/query-types.d.ts +2 -4
  75. package/dist/esm/query-types.d.ts.map +1 -1
  76. package/dist/esm/query.d.ts +17 -39
  77. package/dist/esm/query.d.ts.map +1 -1
  78. package/dist/esm/rest/RESTMutation.d.ts +18 -0
  79. package/dist/esm/rest/RESTMutation.d.ts.map +1 -0
  80. package/dist/esm/rest/RESTQuery.d.ts +24 -0
  81. package/dist/esm/rest/RESTQuery.d.ts.map +1 -0
  82. package/dist/esm/rest/RESTQueryController.d.ts +34 -0
  83. package/dist/esm/rest/RESTQueryController.d.ts.map +1 -0
  84. package/dist/esm/rest/index.d.ts +5 -0
  85. package/dist/esm/rest/index.d.ts.map +1 -0
  86. package/dist/esm/stores/shared.d.ts.map +1 -1
  87. package/dist/esm/testing/MockClient.d.ts +64 -0
  88. package/dist/esm/testing/MockClient.d.ts.map +1 -0
  89. package/dist/esm/testing/auto-generate.d.ts +20 -0
  90. package/dist/esm/testing/auto-generate.d.ts.map +1 -0
  91. package/dist/esm/testing/entity-factory.d.ts +13 -0
  92. package/dist/esm/testing/entity-factory.d.ts.map +1 -0
  93. package/dist/esm/testing/index.d.ts +6 -0
  94. package/dist/esm/testing/index.d.ts.map +1 -0
  95. package/dist/esm/testing/types.d.ts +37 -0
  96. package/dist/esm/testing/types.d.ts.map +1 -0
  97. package/dist/esm/topic/TopicQuery.d.ts +10 -0
  98. package/dist/esm/topic/TopicQuery.d.ts.map +1 -0
  99. package/dist/esm/topic/TopicQueryController.d.ts +43 -0
  100. package/dist/esm/topic/TopicQueryController.d.ts.map +1 -0
  101. package/dist/esm/topic/index.d.ts +3 -0
  102. package/dist/esm/topic/index.d.ts.map +1 -0
  103. package/dist/esm/typeDefs.d.ts +1 -1
  104. package/dist/esm/types.d.ts +9 -4
  105. package/dist/esm/types.d.ts.map +1 -1
  106. package/package.json +51 -4
  107. package/plugin/.claude-plugin/plugin.json +10 -0
  108. package/plugin/agents/fetchium.md +168 -0
  109. package/plugin/docs/api/fetchium-react.md +135 -0
  110. package/plugin/docs/api/fetchium.md +674 -0
  111. package/plugin/docs/api/stores-async.md +219 -0
  112. package/plugin/docs/api/stores-sync.md +133 -0
  113. package/plugin/docs/core/entities.md +351 -0
  114. package/plugin/docs/core/queries.md +600 -0
  115. package/plugin/docs/core/streaming.md +550 -0
  116. package/plugin/docs/core/types.md +374 -0
  117. package/plugin/docs/data/caching.md +298 -0
  118. package/plugin/docs/data/live-data.md +435 -0
  119. package/plugin/docs/data/mutations.md +465 -0
  120. package/plugin/docs/guides/auth.md +318 -0
  121. package/plugin/docs/guides/error-handling.md +351 -0
  122. package/plugin/docs/guides/offline.md +270 -0
  123. package/plugin/docs/guides/testing.md +301 -0
  124. package/plugin/docs/quickstart.md +170 -0
  125. package/plugin/docs/reference/pagination.md +519 -0
  126. package/plugin/docs/reference/rest-queries.md +107 -0
  127. package/plugin/docs/reference/why-signalium.md +364 -0
  128. package/plugin/docs/setup/project-setup.md +319 -0
  129. package/plugin/install.mjs +88 -0
  130. package/plugin/skills/design/SKILL.md +140 -0
  131. package/plugin/skills/teach/SKILL.md +105 -0
  132. package/dist/cjs/development/QueryClient-CpmwggOn.js +0 -2
  133. package/dist/cjs/development/QueryClient-CpmwggOn.js.map +0 -1
  134. package/dist/cjs/production/QueryClient-qi3bR0eD.js +0 -2
  135. package/dist/cjs/production/QueryClient-qi3bR0eD.js.map +0 -1
  136. package/dist/esm/development/QueryClient-DRZtPKFD.js +0 -2568
  137. package/dist/esm/development/QueryClient-DRZtPKFD.js.map +0 -1
  138. package/dist/esm/production/QueryClient-BP0Z1rQV.js.map +0 -1
@@ -0,0 +1,674 @@
1
+ ---
2
+ title: fetchium
3
+ description: API reference for the main fetchium package.
4
+ ---
5
+
6
+ # fetchium
7
+
8
+ API reference for the main `fetchium` package — a data-fetching and query layer built on Signalium's reactive primitives.
9
+
10
+ ```ts
11
+ import {
12
+ Query,
13
+ QueryController,
14
+ fetchQuery,
15
+ queryKeyForClass,
16
+ Mutation,
17
+ getMutation,
18
+ mutationKeyForClass,
19
+ Entity,
20
+ QueryClient,
21
+ NetworkManager,
22
+ NoOpNetworkManager,
23
+ GcManager,
24
+ NoOpGcManager,
25
+ t,
26
+ registerFormat,
27
+ draft,
28
+ QueryClientContext,
29
+ NetworkManagerContext,
30
+ } from 'fetchium';
31
+
32
+ // REST adapter (JSON REST APIs)
33
+ import { RESTQuery, RESTMutation, RESTQueryController } from 'fetchium/rest';
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Classes
39
+
40
+ ### `Query` (abstract)
41
+
42
+ Base class for all query definitions. Extend this to define custom data-fetching logic.
43
+
44
+ #### Static properties
45
+
46
+ | Property | Type | Description |
47
+ | -------- | -------------------------------- | ------------------------------------------------------------ |
48
+ | `cache` | `QueryCacheOptions \| undefined` | Class-level persistent cache settings (maxCount, cacheTime). |
49
+
50
+ #### Static properties
51
+
52
+ | Property | Type | Description |
53
+ | ------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
54
+ | `cache` | `QueryCacheOptions \| undefined` | Class-level persistent cache settings (maxCount, cacheTime). |
55
+ | `controller` | `typeof QueryController` | **(required)** The controller class responsible for sending requests. Set automatically on `RESTQuery`. Custom query types must set this to their own controller class. |
56
+
57
+ #### Instance properties
58
+
59
+ | Property | Type | Description |
60
+ | --------- | -------------------------------------- | ---------------------------------------------------------------------------------------------- |
61
+ | `params` | `Record<string, TypeDef> \| undefined` | Shape definition for query parameters. |
62
+ | `result` | `TypeDefShape` | **(abstract)** Shape definition for the query result. |
63
+ | `config` | `QueryConfigOptions \| undefined` | Instance-level configuration (gcTime, staleTime, retry, etc.). |
64
+ | `context` | `QueryContext` | The query context provided by the `QueryClient`. Available in `getConfig()` and other methods. |
65
+
66
+ #### Methods
67
+
68
+ | Method | Signature | Description |
69
+ | ---------------- | ------------------------------------- | ------------------------------------------------------------------------------------------- |
70
+ | `getIdentityKey` | `(): unknown` | **(abstract)** Returns a value used to compute the cache/identity key for this query class. |
71
+ | `refetch` | `(): void` | Triggers a refetch of this query, bypassing staleTime. |
72
+ | `getConfig` | `(): QueryConfigOptions \| undefined` | Optional. Dynamically compute config at execution time. |
73
+
74
+ ---
75
+
76
+ ### `RESTQuery` extends `Query`
77
+
78
+ Convenience base class for REST/JSON queries. Handles URL construction, search params, body serialization, and pagination.
79
+
80
+ #### Instance properties
81
+
82
+ | Property | Type | Default | Description |
83
+ | ---------------- | ------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
84
+ | `method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH'` | `'GET'` | HTTP method. |
85
+ | `path` | `string \| undefined` | — | URL path. Use template literal interpolation with `this.params` references. |
86
+ | `searchParams` | `Record<string, unknown> \| undefined` | — | Query string parameters. |
87
+ | `body` | `Record<string, unknown> \| undefined` | — | Request body (JSON-serialized). |
88
+ | `headers` | `HeadersInit \| undefined` | — | Custom HTTP headers. |
89
+ | `requestOptions` | `QueryRequestOptions \| undefined` | — | Additional fetch options (credentials, mode, baseUrl, etc.). |
90
+ | `fetchNext` | `FetchNextConfig \| undefined` | — | Static pagination config. Values can be FieldRefs (e.g. `this.result.nextCursor`). |
91
+ | `response` | `Response \| undefined` | — | The raw HTTP `Response` from the last fetch. Set by `RESTQueryController` after each request completes. Available in `getConfig()`. |
92
+
93
+ #### `getIdentityKey()` default
94
+
95
+ Returns `"${method}:${path}"`.
96
+
97
+ #### Optional method overrides
98
+
99
+ | Method | Signature | Description |
100
+ | ------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------- |
101
+ | `getPath` | `(): string \| undefined` | Dynamically compute the URL path. |
102
+ | `getMethod` | `(): string` | Dynamically compute the HTTP method. |
103
+ | `getSearchParams` | `(): Record<string, unknown> \| undefined` | Dynamically compute search params. |
104
+ | `getBody` | `(): Record<string, unknown> \| undefined` | Dynamically compute the request body. |
105
+ | `getRequestOptions` | `(): QueryRequestOptions \| undefined` | Dynamically compute fetch options. |
106
+ | `getFetchNext` | `(): FetchNextConfig \| undefined` | Dynamically compute pagination config. Takes priority over the static `fetchNext` field. |
107
+
108
+ #### Example
109
+
110
+ ```ts
111
+ class GetUser extends RESTQuery {
112
+ params = { id: t.string };
113
+
114
+ path = `/api/users/${this.params.id}`;
115
+
116
+ result = {
117
+ id: t.id,
118
+ name: t.string,
119
+ email: t.string,
120
+ };
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ### `Entity`
127
+
128
+ Base class for entity definitions. Entities are normalized, identity-stable proxy objects managed by the `QueryClient`.
129
+
130
+ #### Static properties
131
+
132
+ | Property | Type | Description |
133
+ | -------- | ---------------------------------- | ------------------------------------------------------------------------------ |
134
+ | `cache` | `{ gcTime?: number } \| undefined` | In-memory GC time in minutes. `0` = next-tick eviction, `Infinity` = never GC. |
135
+
136
+ #### Optional instance methods
137
+
138
+ | Method | Signature | Description |
139
+ | ------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
140
+ | `__subscribe` | `(onEvent: (event: MutationEvent) => void) => (() => void) \| undefined` | Subscribe to external mutation events for this entity (e.g. WebSocket push). Return a cleanup function. |
141
+
142
+ #### Example
143
+
144
+ ```ts
145
+ class User extends Entity {
146
+ static cache = { gcTime: 10 };
147
+
148
+ __typename = t.typename('User');
149
+ id = t.id;
150
+
151
+ name = t.string;
152
+ email = t.optional(t.string);
153
+ }
154
+ ```
155
+
156
+ ---
157
+
158
+ ### `Mutation` (abstract)
159
+
160
+ Base class for mutation definitions.
161
+
162
+ #### Static properties
163
+
164
+ | Property | Type | Description |
165
+ | ------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
166
+ | `controller` | `typeof QueryController` | **(required)** The controller class that handles sending this mutation. Set automatically on `RESTMutation`. Custom mutation types must set this to their own controller class. |
167
+
168
+ #### Instance properties
169
+
170
+ | Property | Type | Description |
171
+ | ------------------- | ------------------------------------ | ------------------------------------------------------------------------- |
172
+ | `params` | `TypeDefShape \| undefined` | Shape definition for mutation input parameters. |
173
+ | `result` | `TypeDefShape \| undefined` | Shape definition for the mutation response. |
174
+ | `optimisticUpdates` | `boolean \| undefined` | When `true`, applies effects optimistically before the server responds. |
175
+ | `config` | `MutationConfigOptions \| undefined` | Mutation configuration (retry settings). |
176
+ | `effects` | `MutationEffects \| undefined` | Static entity effects (creates, updates, deletes) and query invalidation. |
177
+ | `context` | `QueryContext` | The query context provided by the `QueryClient`. |
178
+
179
+ #### Methods
180
+
181
+ | Method | Signature | Description |
182
+ | ---------------- | --------------------- | ------------------------------------------------------------------------- |
183
+ | `getIdentityKey` | `(): unknown` | **(abstract)** Returns a value used to compute the mutation identity key. |
184
+ | `getEffects` | `(): MutationEffects` | Optional. Dynamically compute entity effects at execution time. |
185
+
186
+ ---
187
+
188
+ ### `RESTMutation` extends `Mutation`
189
+
190
+ Convenience base class for REST/JSON mutations.
191
+
192
+ #### Instance properties
193
+
194
+ | Property | Type | Default | Description |
195
+ | ---------------- | ---------------------------------------- | -------- | ----------------------------------------------- |
196
+ | `path` | `string \| undefined` | — | URL path. |
197
+ | `method` | `'POST' \| 'PUT' \| 'DELETE' \| 'PATCH'` | `'POST'` | HTTP method. |
198
+ | `body` | `Record<string, unknown> \| undefined` | — | Request body shape. No body is sent if omitted. |
199
+ | `headers` | `HeadersInit \| undefined` | — | Custom HTTP headers. |
200
+ | `requestOptions` | `QueryRequestOptions \| undefined` | — | Additional fetch options. |
201
+
202
+ #### `getIdentityKey()` default
203
+
204
+ Returns `"${method}:${path}"`.
205
+
206
+ #### Optional method overrides
207
+
208
+ | Method | Signature | Description |
209
+ | ------------------- | ------------------------------------------ | ------------------------------------- |
210
+ | `getPath` | `(): string \| undefined` | Dynamically compute the URL path. |
211
+ | `getMethod` | `(): string` | Dynamically compute the HTTP method. |
212
+ | `getBody` | `(): Record<string, unknown> \| undefined` | Dynamically compute the request body. |
213
+ | `getRequestOptions` | `(): QueryRequestOptions \| undefined` | Dynamically compute fetch options. |
214
+
215
+ ---
216
+
217
+ ### `QueryClient`
218
+
219
+ Central coordinator for queries, mutations, entity storage, caching, and garbage collection.
220
+
221
+ #### Constructor
222
+
223
+ ```ts
224
+ new QueryClient(config: QueryClientConfig)
225
+ ```
226
+
227
+ | Field | Type | Default | Description |
228
+ | -------------------- | ---------------------------- | ---------------------- | ------------------------------------------------------------------------------------ |
229
+ | `store` | `QueryStore` | — | **(required)** Persistent storage backend. |
230
+ | `controllers` | `QueryController[]` | `[]` | Transport controllers (e.g. `new RESTQueryController({ fetch, baseUrl })`). |
231
+ | `log` | `LogContext \| undefined` | `console` | Logger with `error`, `warn`, `info`, `debug` methods. |
232
+ | `evictionMultiplier` | `number \| undefined` | `1` | Scales all GC times for testing. Set to `0.001` to make timers fire in milliseconds. |
233
+ | `networkManager` | `NetworkManager` | `new NetworkManager()` | Tracks network connectivity. |
234
+ | `gcManager` | `GcManager \| NoOpGcManager` | Auto-detected | GC manager. Uses `NoOpGcManager` on the server. |
235
+
236
+ #### Methods
237
+
238
+ | Method | Signature | Description |
239
+ | -------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
240
+ | `getContext` | `(): QueryContext` | Returns the `QueryContext` passed at construction. |
241
+ | `applyMutationEvent` | `(event: MutationEvent): void` | Applies an external mutation event (create/update/delete) to the entity store. |
242
+ | `invalidateQueries` | `(targets: ReadonlyArray<InvalidateTarget>): void` | Marks matching query instances as stale. Accepts query classes and optional param subsets for filtering. |
243
+ | `destroy` | `(): void` | Tears down the GC manager, network manager, and all caches. |
244
+
245
+ ---
246
+
247
+ ### `QueryController` (abstract)
248
+
249
+ Base class for transport adapters. A controller handles sending queries and mutations for all query/mutation classes that declare it via `static controller`. Register controllers with `QueryClient` at construction time.
250
+
251
+ #### Methods
252
+
253
+ | Method | Signature | Description |
254
+ | ----------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
255
+ | `register` | `(queryClient: IQueryClientForController): void` | Called once when the controller is registered with a `QueryClient`. Override to do setup (e.g. open a WebSocket connection). |
256
+ | `send` | `(ctx: Query, signal: AbortSignal): Promise<unknown>` | **(abstract)** Send a query and return the raw response data. |
257
+ | `sendNext` | `(ctx: Query, signal: AbortSignal): Promise<unknown>` | Optional. Send the next-page request for a paginated query. |
258
+ | `hasNext` | `(ctx: Query): boolean` | Optional. Return `true` if more pages are available for the current result. |
259
+ | `sendMutation` | `(ctx: Mutation, signal: AbortSignal): Promise<unknown>` | Optional. Send a mutation and return the raw response data. |
260
+ | `onNetworkStatusChange` | `(isOnline: boolean): void` | Optional. Called when the network comes online or goes offline. |
261
+ | `destroy` | `(): void` | Optional. Called when the `QueryClient` is destroyed. Clean up connections or timers. |
262
+
263
+ #### Protected properties
264
+
265
+ | Property | Type | Description |
266
+ | ------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------ |
267
+ | `queryClient` | `IQueryClientForController \| undefined` | Set by `register()`. Use to access the shared query context via `this.queryClient.getContext()`. |
268
+
269
+ #### Example
270
+
271
+ ```ts
272
+ import { QueryController } from 'fetchium';
273
+ import type { Query } from 'fetchium';
274
+
275
+ class GraphQLController extends QueryController {
276
+ async send(ctx: Query, signal: AbortSignal): Promise<unknown> {
277
+ const q = ctx as GraphQLQuery;
278
+ const response = await fetch('/graphql', {
279
+ method: 'POST',
280
+ headers: { 'Content-Type': 'application/json' },
281
+ body: JSON.stringify({ query: q.query, variables: q.variables }),
282
+ signal,
283
+ });
284
+ const json = await response.json();
285
+ if (json.errors?.length) throw new Error(json.errors[0].message);
286
+ return json.data;
287
+ }
288
+ }
289
+
290
+ new QueryClient({
291
+ store,
292
+ controllers: [new GraphQLController()],
293
+ });
294
+ ```
295
+
296
+ ---
297
+
298
+ ### `RESTQueryController` extends `QueryController`
299
+
300
+ Transport controller for `RESTQuery` and `RESTMutation`. Handles URL construction, JSON serialization, search params, pagination, and `baseUrl` resolution.
301
+
302
+ Import from `fetchium/rest`.
303
+
304
+ #### Constructor
305
+
306
+ ```ts
307
+ new RESTQueryController(options?: RESTQueryControllerOptions)
308
+ ```
309
+
310
+ | Option | Type | Description |
311
+ | --------- | --------------------------------------------------------- | ---------------------------------------------------------------- |
312
+ | `fetch` | `(url: string, init?: RequestInit) => Promise<Response>` | The fetch implementation to use. Defaults to `globalThis.fetch`. |
313
+ | `baseUrl` | `string \| Signal<string> \| (() => string) \| undefined` | Base URL prepended to all request paths. |
314
+
315
+ ---
316
+
317
+ ### `NetworkManager`
318
+
319
+ Signal-based network connectivity tracker. Automatically detects browser online/offline events.
320
+
321
+ #### Constructor
322
+
323
+ ```ts
324
+ new NetworkManager(initialStatus?: boolean)
325
+ ```
326
+
327
+ | Parameter | Type | Default | Description |
328
+ | --------------- | --------- | ------------- | ----------------------------------------------------------- |
329
+ | `initialStatus` | `boolean` | Auto-detected | Initial online status. If omitted, uses `navigator.onLine`. |
330
+
331
+ #### Properties and methods
332
+
333
+ | Member | Type / Signature | Description |
334
+ | --------------------- | ------------------------- | ------------------------------------------------------------------------------------ |
335
+ | `isOnline` | `boolean` (getter) | Returns `true` if the network is currently online. Manual override takes precedence. |
336
+ | `setNetworkStatus` | `(online: boolean): void` | Manually set the network status. |
337
+ | `clearManualOverride` | `(): void` | Clear any manual override and return to automatic detection. |
338
+ | `getOnlineSignal` | `(): Signal<boolean>` | Returns the underlying reactive Signal for online status. |
339
+ | `destroy` | `(): void` | Removes event listeners and cleans up resources. |
340
+
341
+ ---
342
+
343
+ ### `NoOpNetworkManager`
344
+
345
+ SSR-safe no-op implementation of `NetworkManager`. Always reports `isOnline = true`.
346
+
347
+ | Member | Type / Signature | Description |
348
+ | --------------------- | ------------------------- | -------------------------------- |
349
+ | `isOnline` | `boolean` (getter) | Always returns `true`. |
350
+ | `setNetworkStatus` | `(online: boolean): void` | No-op. |
351
+ | `clearManualOverride` | `(): void` | No-op. |
352
+ | `getOnlineSignal` | `(): Signal<boolean>` | Returns a static `Signal(true)`. |
353
+ | `destroy` | `(): void` | No-op. |
354
+
355
+ ---
356
+
357
+ ### `GcManager`
358
+
359
+ Bucket-based in-memory garbage collection. Each unique `gcTime` gets its own interval with two rotating sets. Minimum eviction delay is approximately `gcTime`; maximum is approximately `2 * gcTime`.
360
+
361
+ #### Constructor
362
+
363
+ ```ts
364
+ new GcManager(
365
+ onEvict: (key: number, type: GcKeyType) => void,
366
+ multiplier?: number,
367
+ )
368
+ ```
369
+
370
+ | Parameter | Type | Default | Description |
371
+ | ------------ | ---------------------------------------- | ------- | -------------------------------------------------------------- |
372
+ | `onEvict` | `(key: number, type: GcKeyType) => void` | — | Callback invoked when a key is evicted. |
373
+ | `multiplier` | `number` | `1` | Multiplier applied to `gcTime` intervals (useful for testing). |
374
+
375
+ #### Methods
376
+
377
+ | Method | Signature | Description |
378
+ | ---------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
379
+ | `schedule` | `(key: number, gcTime: number, type: GcKeyType): void` | Schedule a key for eviction. `gcTime = 0` evicts on next microtask. `gcTime = Infinity` never evicts. |
380
+ | `cancel` | `(key: number, gcTime: number): void` | Cancel a pending eviction. |
381
+ | `destroy` | `(): void` | Clears all intervals and pending entries. |
382
+
383
+ ---
384
+
385
+ ### `NoOpGcManager`
386
+
387
+ No-op garbage collection manager. All methods are no-ops.
388
+
389
+ | Method | Signature |
390
+ | ---------- | ------------------------------------------------------ |
391
+ | `schedule` | `(key: number, gcTime: number, type: GcKeyType): void` |
392
+ | `cancel` | `(key: number, gcTime: number): void` |
393
+ | `destroy` | `(): void` |
394
+
395
+ ---
396
+
397
+ ## Functions
398
+
399
+ ### `fetchQuery`
400
+
401
+ ```ts
402
+ function fetchQuery<T extends Query>(
403
+ QueryClass: new () => T,
404
+ params?: ExtractQueryParams<T>,
405
+ ): QueryPromise<T>;
406
+ ```
407
+
408
+ Fetches a query reactively. Must be called within a reactive context where `QueryClientContext` is provided. Returns a `QueryPromise` (a `DiscriminatedReactivePromise`) that resolves to the query result.
409
+
410
+ | Parameter | Type | Description |
411
+ | ------------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
412
+ | `QueryClass` | `new () => T` | The query class to instantiate and execute. |
413
+ | `params` | `ExtractQueryParams<T>` | Parameters matching the query's `params` shape. Optional if the query has no required params. Values can be Signalium `Signal`s for reactive parameter changes. |
414
+
415
+ **Returns:** `QueryPromise<T>` — a reactive promise with `.value`, `.isPending`, `.isResolved`, `.isRejected`, `.error`.
416
+
417
+ ---
418
+
419
+ ### `getMutation`
420
+
421
+ ```ts
422
+ function getMutation<T extends Mutation>(
423
+ MutationClass: new () => T,
424
+ ): ReactiveTask<ExtractType<T['result']>, [ExtractType<T['params']>]>;
425
+ ```
426
+
427
+ Returns a `ReactiveTask` for executing a mutation. Must be called within a reactive context where `QueryClientContext` is provided.
428
+
429
+ | Parameter | Type | Description |
430
+ | --------------- | ------------- | ---------------------------------- |
431
+ | `MutationClass` | `new () => T` | The mutation class to instantiate. |
432
+
433
+ **Returns:** `ReactiveTask` — call `.run(params)` to execute the mutation.
434
+
435
+ ---
436
+
437
+ ### `queryKeyForClass`
438
+
439
+ ```ts
440
+ function queryKeyForClass(cls: new () => Query, params: unknown): number;
441
+ ```
442
+
443
+ Computes the numeric cache key for a query class and params combination. Useful for cache invalidation or inspection.
444
+
445
+ ---
446
+
447
+ ### `mutationKeyForClass`
448
+
449
+ ```ts
450
+ function mutationKeyForClass(cls: new () => Mutation): string;
451
+ ```
452
+
453
+ Returns the string identity key for a mutation class. Derived from the mutation's `getIdentityKey()`.
454
+
455
+ ---
456
+
457
+ ### `registerFormat`
458
+
459
+ ```ts
460
+ function registerFormat<Input extends Mask.STRING | Mask.NUMBER, T>(
461
+ name: string,
462
+ type: Input,
463
+ parse: (value: Input extends Mask.STRING ? string : number) => T,
464
+ serialize: (value: T) => Input extends Mask.STRING ? string : number,
465
+ options?: { eager?: boolean },
466
+ ): void;
467
+ ```
468
+
469
+ Registers a custom format for use with `t.format(name)`. Built-in formats include `'date'` and `'date-time'`.
470
+
471
+ | Parameter | Type | Description |
472
+ | --------------- | ---------------------------- | -------------------------------------------------------------------------------------------------- |
473
+ | `name` | `string` | Format name. Use with `t.format(name)`. |
474
+ | `type` | `Mask.STRING \| Mask.NUMBER` | The underlying wire type (string or number). |
475
+ | `parse` | `(value) => T` | Converts the raw wire value into the formatted type. |
476
+ | `serialize` | `(value) => wire` | Converts the formatted type back to the wire value. |
477
+ | `options.eager` | `boolean` | If `true` (default), parsing runs eagerly during entity construction. If `false`, parsing is lazy. |
478
+
479
+ To add TypeScript types for custom formats, use module augmentation:
480
+
481
+ ```ts
482
+ declare global {
483
+ namespace SignaliumQuery {
484
+ interface FormatRegistry {
485
+ 'my-format': MyType;
486
+ }
487
+ }
488
+ }
489
+ ```
490
+
491
+ ---
492
+
493
+ ### `draft`
494
+
495
+ ```ts
496
+ function draft<T>(value: T): Draft<T>;
497
+ ```
498
+
499
+ Deep clones an entity or object, returning a plain mutable copy. The draft is not an entity proxy and can be freely modified before being passed to a mutation.
500
+
501
+ | Parameter | Type | Description |
502
+ | --------- | ---- | ------------------------------ |
503
+ | `value` | `T` | The entity or object to clone. |
504
+
505
+ **Returns:** `Draft<T>` — a recursively mutable deep clone.
506
+
507
+ ---
508
+
509
+ ## The `t` Type DSL
510
+
511
+ The `t` object provides a declarative type definition DSL for describing query parameters, results, and entity shapes.
512
+
513
+ ### Primitive types
514
+
515
+ | Property | Type | Description |
516
+ | ------------- | --------------------------- | ------------------------------------------------------------------------------------------- |
517
+ | `t.string` | `TypeDef<string>` | String type. |
518
+ | `t.number` | `TypeDef<number>` | Number type. |
519
+ | `t.boolean` | `TypeDef<boolean>` | Boolean type. |
520
+ | `t.null` | `TypeDef<null>` | Null literal type. |
521
+ | `t.undefined` | `TypeDef<undefined>` | Undefined literal type. |
522
+ | `t.id` | `TypeDef<string \| number>` | Identity field marker. Marks the field as the entity's unique ID. Accepts string or number. |
523
+
524
+ ### Composite types
525
+
526
+ | Method | Signature | Description |
527
+ | ---------- | -------------------------------------------------- | -------------------------------------------------------------------------------- |
528
+ | `t.array` | `(type: TypeDef<T>) => TypeDef<T[]>` | Array of the given element type. |
529
+ | `t.object` | `(shape: Record<string, TypeDef>) => TypeDef<...>` | Object with the given field shapes. |
530
+ | `t.record` | `(type: TypeDef<T>) => TypeDef<Record<string, T>>` | Record (dictionary) with string keys and values of the given type. |
531
+ | `t.union` | `(...types: TypeDef[]) => TypeDef<...>` | Discriminated union. Object types must have a typename field for discrimination. |
532
+ | `t.entity` | `(cls: new () => Entity) => TypeDef<Entity>` | Reference to a normalized entity type. |
533
+
534
+ ### Modifiers
535
+
536
+ | Method | Signature | Description |
537
+ | ------------ | ------------------------------------------------------- | ---------------------------------------------------------- |
538
+ | `t.optional` | `(type: TypeDef<T>) => TypeDef<T \| undefined>` | Makes a type optional (allows `undefined`). |
539
+ | `t.nullable` | `(type: TypeDef<T>) => TypeDef<T \| null>` | Makes a type nullable (allows `null`). |
540
+ | `t.nullish` | `(type: TypeDef<T>) => TypeDef<T \| undefined \| null>` | Makes a type nullish (allows both `undefined` and `null`). |
541
+
542
+ ### Constants and enums
543
+
544
+ | Method | Signature | Description |
545
+ | ------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------ |
546
+ | `t.const` | `(value: T) => TypeDef<T>` | Constant literal value. |
547
+ | `t.enum` | `(...values: T[]) => TypeDef<T[number]>` | Enum of allowed values (strings, numbers, or booleans). |
548
+ | `t.enum.caseInsensitive` | `(...values: T[]) => TypeDef<T[number]>` | Case-insensitive enum. String values match case-insensitively but return canonical casing. |
549
+
550
+ ### Formats
551
+
552
+ | Method | Signature | Description |
553
+ | ---------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------- |
554
+ | `t.format` | `(name: string) => TypeDef<FormatRegistry[name]>` | A formatted value. Built-in: `'date'` (YYYY-MM-DD to Date), `'date-time'` (ISO 8601 to Date). |
555
+
556
+ ### Result parsing
557
+
558
+ | Method | Signature | Description |
559
+ | ---------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
560
+ | `t.result` | `(type: TypeDef<T>) => TypeDef<ParseResult<T>>` | Wraps a type in a `ParseResult<T>` (`{ success: true, value: T } \| { success: false, error: Error }`). Individual fields that fail validation produce an error result instead of throwing. |
561
+
562
+ ### Live data
563
+
564
+ | Method | Signature | Description |
565
+ | ------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
566
+ | `t.liveArray` | `(entity: EntityClass \| EntityClass[], opts?: LiveArrayOptions) => TypeDef<E[]>` | A live array that automatically updates when matching entities are created, updated, or deleted via mutation events. |
567
+ | `t.liveValue` | `(type: TypeDef<V>, entity: EntityClass \| EntityClass[], opts: LiveValueOptions) => TypeDef<V>` | A live derived value that recomputes when matching entities change. Requires `onCreate`, `onUpdate`, and `onDelete` callbacks. |
568
+
569
+ #### `LiveArrayOptions<E>`
570
+
571
+ | Property | Type | Description |
572
+ | ------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
573
+ | `constraints` | `ConstraintDef<E>` | Filter which entities are included. Can be a `Record<string, unknown>` or an array of `[EntityClass, constraintMap]` tuples. Constraint values can be FieldRefs. |
574
+ | `sort` | `(a: E, b: E) => number` | Sort comparator for the array. |
575
+
576
+ #### `LiveValueOptions<V, E>`
577
+
578
+ | Property | Type | Description |
579
+ | ------------- | ---------------------------- | ---------------------------------------------------------------- |
580
+ | `constraints` | `ConstraintDef<E>` | Filter which entities trigger recomputation. |
581
+ | `onCreate` | `(value: V, entity: E) => V` | Called when a matching entity is created. Returns the new value. |
582
+ | `onUpdate` | `(value: V, entity: E) => V` | Called when a matching entity is updated. Returns the new value. |
583
+ | `onDelete` | `(value: V, entity: E) => V` | Called when a matching entity is deleted. Returns the new value. |
584
+
585
+ ---
586
+
587
+ ## Contexts
588
+
589
+ | Context | Type | Default | Description |
590
+ | ----------------------- | ----------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------- |
591
+ | `QueryClientContext` | `Context<QueryClient \| undefined>` | `undefined` | Signalium context for the `QueryClient`. Must be provided for `fetchQuery` and `getMutation` to work. |
592
+ | `NetworkManagerContext` | `Context<NetworkManager>` | `defaultNetworkManager` | Signalium context for the `NetworkManager`. |
593
+
594
+ ---
595
+
596
+ ## Types
597
+
598
+ ### Query types
599
+
600
+ | Type | Definition | Description |
601
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
602
+ | `QueryPromise<T extends Query>` | `DiscriminatedReactivePromise<QueryResult<T>>` | The return type of `fetchQuery`. A reactive promise. |
603
+ | `QueryResult<T extends Query>` | `ExtractType<T['result']> & { __refetch(), __fetchNext(), __hasNext, __isFetchingNext }` | The resolved value of a query. Includes pagination helpers. |
604
+ | `QueryCacheOptions` | `{ maxCount?: number; cacheTime?: number }` | Persistent storage cache settings. `cacheTime` is in minutes (default: 1440 / 24 hours). `maxCount` is the LRU queue size. |
605
+ | `QueryConfigOptions` | `{ gcTime?, staleTime?, debounce?, networkMode?, retry?, refreshStaleOnReconnect?, subscribe? }` | Instance-level query configuration. See property table below. |
606
+ | `QueryRequestOptions` | `{ baseUrl?, credentials?, mode?, cache?, redirect?, referrer?, referrerPolicy?, integrity?, keepalive?, signal? }` | Extended fetch options for individual queries. |
607
+ | `QueryContext` | `{ fetch, baseUrl?, log?, evictionMultiplier? }` | Context object provided to the `QueryClient`. |
608
+ | `QueryParams` | `Record<string, string \| number \| boolean \| undefined \| null \| Signal<...> \| unknown[] \| Record<string, unknown>>` | The shape of query parameters at runtime. |
609
+ | `FetchNextConfig` | `{ url?: unknown; searchParams?: Record<string, unknown> }` | Pagination configuration. Values can be FieldRefs. |
610
+
611
+ #### `QueryConfigOptions` properties
612
+
613
+ | Property | Type | Default | Description |
614
+ | ------------------------- | ---------------------------------- | --------------------------- | ------------------------------------------------------------------------ |
615
+ | `gcTime` | `number` | `5` | In-memory eviction time in minutes. `0` = next-tick, `Infinity` = never. |
616
+ | `staleTime` | `number` | `0` | Milliseconds data is considered fresh. `0` = always stale. |
617
+ | `debounce` | `number` | `0` | Milliseconds to debounce param-change refetches. |
618
+ | `networkMode` | `NetworkMode` | `NetworkMode.Online` | When to allow fetching. |
619
+ | `retry` | `RetryConfig \| number \| boolean` | `3` (client) / `0` (server) | Retry configuration. |
620
+ | `refreshStaleOnReconnect` | `boolean` | `true` | Whether to refetch stale queries when network reconnects. |
621
+ | `subscribe` | `(onEvent) => () => void` | — | Subscribe to external events that should trigger refetches. |
622
+
623
+ ### Mutation types
624
+
625
+ | Type | Definition | Description |
626
+ | ----------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
627
+ | `MutationConfigOptions` | `{ retry?: RetryConfig \| number \| false }` | Mutation retry configuration. |
628
+ | `MutationEffects` | `{ creates?, updates?, deletes?, invalidates? }` | Mutation side effects. Entity effects are `ReadonlyArray<readonly [EntityClassOrTypename, unknown]>`. `invalidates` is `ReadonlyArray<InvalidateTarget>`. |
629
+ | `InvalidateTarget` | `QueryClass \| readonly [QueryClass, Record<string, unknown>]` | Target for query invalidation. Class alone matches all instances; tuple with param subset matches only instances whose params contain those values. |
630
+ | `MutationEvent` | `CreateEvent \| UpdateEvent \| DeleteEvent` | Union of mutation event types. |
631
+ | `CreateEvent` | `{ type: 'create'; typename: string; data: Record<string, unknown>; id?: unknown }` | Entity creation event. |
632
+ | `UpdateEvent` | `{ type: 'update'; typename: string; data: Record<string, unknown>; id?: unknown }` | Entity update event. |
633
+ | `DeleteEvent` | `{ type: 'delete'; typename: string; data: string \| number \| Record<string, unknown>; id?: unknown }` | Entity deletion event. `data` can be just the ID. |
634
+
635
+ ### Network types
636
+
637
+ | Type | Definition | Description |
638
+ | ------------- | --------------------------------------- | ------------------------------------------- |
639
+ | `NetworkMode` | `enum { Always, Online, OfflineFirst }` | Controls when queries are allowed to fetch. |
640
+
641
+ #### `NetworkMode` values
642
+
643
+ | Value | Description |
644
+ | -------------------------- | ----------------------------------------------- |
645
+ | `NetworkMode.Always` | Always fetch regardless of network status. |
646
+ | `NetworkMode.Online` | Only fetch when online (default). |
647
+ | `NetworkMode.OfflineFirst` | Fetch if cached data exists, even when offline. |
648
+
649
+ ### Retry types
650
+
651
+ | Type | Definition | Description |
652
+ | ------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
653
+ | `RetryConfig` | `{ retries: number; retryDelay?: (attemptIndex: number) => number }` | Retry configuration. Default delay: exponential backoff (`1000ms * 2^attempt`). |
654
+
655
+ ### Utility types
656
+
657
+ | Type | Definition | Description |
658
+ | ----------------------- | ------------------------------------ | ----------------------------------------------------------------------------- |
659
+ | `TypeDef<T>` | Branded phantom type | Represents a type definition in the public API. |
660
+ | `TypeDefShape` | `Record<string, TypeDef> \| TypeDef` | A shape for query results or mutation params. |
661
+ | `ExtractType<T>` | Conditional type | Extracts the TypeScript type from a `TypeDef<T>`. |
662
+ | `ExtractQueryParams<T>` | Conditional type | Extracts the params type from a `Query` subclass. |
663
+ | `Draft<T>` | Recursive mapped type | Recursively removes `readonly` from all properties. Return type of `draft()`. |
664
+ | `ParseResult<T>` | `ParseSuccess<T> \| ParseError` | Result of `t.result()` parsing. |
665
+ | `ParseSuccess<T>` | `{ success: true; value: T }` | Successful parse. |
666
+ | `ParseError` | `{ success: false; error: Error }` | Failed parse. |
667
+
668
+ ### Store types
669
+
670
+ | Type | Definition | Description |
671
+ | -------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
672
+ | `QueryStore` | Interface | Storage backend interface. See [stores/sync](/api/stores-sync) and [stores/async](/api/stores-async). |
673
+ | `CachedQuery` | `{ value: unknown; refIds: Set<number> \| undefined; updatedAt: number; preloadedEntities?: PreloadedEntityMap }` | Cached query data returned by `QueryStore.loadQuery()`. |
674
+ | `PreloadedEntityMap` | `Map<number, Record<string, unknown>>` | Pre-loaded entity data for hydration. |