@structyl/api-client 1.0.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.
@@ -0,0 +1,230 @@
1
+ 'use strict';
2
+
3
+ // src/cache.ts
4
+ function serializeKey(key) {
5
+ return JSON.stringify(key);
6
+ }
7
+ var QueryCache = class {
8
+ constructor(gcTime = 5 * 6e4) {
9
+ this.entries = /* @__PURE__ */ new Map();
10
+ this.subscribers = /* @__PURE__ */ new Map();
11
+ this.inFlight = /* @__PURE__ */ new Map();
12
+ // Discards responses from requests that were superseded (e.g. after cancelQueries)
13
+ this.generations = /* @__PURE__ */ new Map();
14
+ // Evicts entries when no subscriber remains for longer than gcTime
15
+ this.gcTimers = /* @__PURE__ */ new Map();
16
+ // AbortControllers for in-flight requests
17
+ this.abortControllers = /* @__PURE__ */ new Map();
18
+ // Global subscribers (for persistence and devtools)
19
+ this.globalSubscribers = /* @__PURE__ */ new Set();
20
+ this.gcTime = gcTime;
21
+ }
22
+ get(key) {
23
+ return this.entries.get(key);
24
+ }
25
+ subscribe(key, fn) {
26
+ const pending = this.gcTimers.get(key);
27
+ if (pending !== void 0) {
28
+ clearTimeout(pending);
29
+ this.gcTimers.delete(key);
30
+ }
31
+ if (!this.subscribers.has(key)) this.subscribers.set(key, /* @__PURE__ */ new Set());
32
+ this.subscribers.get(key).add(fn);
33
+ return () => {
34
+ const set = this.subscribers.get(key);
35
+ if (!set) return;
36
+ set.delete(fn);
37
+ if (set.size === 0) {
38
+ this.subscribers.delete(key);
39
+ const timer = setTimeout(() => {
40
+ this.entries.delete(key);
41
+ this.generations.delete(key);
42
+ this.gcTimers.delete(key);
43
+ }, this.gcTime);
44
+ this.gcTimers.set(key, timer);
45
+ }
46
+ };
47
+ }
48
+ subscribeGlobal(fn) {
49
+ this.globalSubscribers.add(fn);
50
+ return () => this.globalSubscribers.delete(fn);
51
+ }
52
+ notify(key) {
53
+ this.subscribers.get(key)?.forEach((fn) => fn());
54
+ this.globalSubscribers.forEach((fn) => fn());
55
+ }
56
+ setData(key, data, staleTime) {
57
+ this.entries.set(key, {
58
+ data,
59
+ error: void 0,
60
+ status: "success",
61
+ updatedAt: Date.now(),
62
+ staleTime
63
+ });
64
+ this.inFlight.delete(key);
65
+ this.notify(key);
66
+ }
67
+ setError(key, error) {
68
+ const existing = this.entries.get(key);
69
+ this.entries.set(key, {
70
+ data: existing?.data,
71
+ error,
72
+ status: "error",
73
+ updatedAt: Date.now(),
74
+ staleTime: 0
75
+ });
76
+ this.inFlight.delete(key);
77
+ this.notify(key);
78
+ }
79
+ setLoading(key) {
80
+ const existing = this.entries.get(key);
81
+ if (existing?.status === "loading") return;
82
+ this.entries.set(key, {
83
+ data: existing?.data,
84
+ error: void 0,
85
+ status: "loading",
86
+ updatedAt: existing?.updatedAt ?? 0,
87
+ staleTime: existing?.staleTime ?? 0
88
+ });
89
+ this.notify(key);
90
+ }
91
+ isStale(key) {
92
+ const e = this.entries.get(key);
93
+ if (!e || e.status === "idle" || e.status === "error") return true;
94
+ if (e.status === "loading") return false;
95
+ return e.updatedAt === 0 || Date.now() - e.updatedAt > e.staleTime;
96
+ }
97
+ // True only when markStale() was explicitly called on a successful entry.
98
+ // Used to detect *external* invalidation (e.g. after a mutation) vs time-based
99
+ // staleness, so the re-fetch effect doesn't loop when staleTime is 0.
100
+ isExternallyInvalidated(key) {
101
+ const e = this.entries.get(key);
102
+ return !!e && e.status === "success" && e.updatedAt === 0;
103
+ }
104
+ getInFlight(key) {
105
+ return this.inFlight.get(key);
106
+ }
107
+ setInFlight(key, p) {
108
+ this.inFlight.set(key, p);
109
+ }
110
+ clearInFlight(key) {
111
+ this.inFlight.delete(key);
112
+ }
113
+ // Returns the new generation. Does NOT touch inFlight — call clearInFlight separately.
114
+ bumpGeneration(key) {
115
+ const g = (this.generations.get(key) ?? 0) + 1;
116
+ this.generations.set(key, g);
117
+ return g;
118
+ }
119
+ getGeneration(key) {
120
+ return this.generations.get(key) ?? 0;
121
+ }
122
+ // Sets updatedAt = 0 as the sentinel for "externally invalidated"
123
+ markStale(key) {
124
+ const e = this.entries.get(key);
125
+ if (e) {
126
+ this.entries.set(key, { ...e, updatedAt: 0 });
127
+ this.notify(key);
128
+ }
129
+ }
130
+ // AbortController management
131
+ setAbortController(key, ctrl) {
132
+ this.abortControllers.set(key, ctrl);
133
+ }
134
+ getAbortController(key) {
135
+ return this.abortControllers.get(key);
136
+ }
137
+ clearAbortController(key) {
138
+ this.abortControllers.delete(key);
139
+ }
140
+ clear() {
141
+ for (const timer of this.gcTimers.values()) clearTimeout(timer);
142
+ this.gcTimers.clear();
143
+ for (const ctrl of this.abortControllers.values()) ctrl.abort();
144
+ this.abortControllers.clear();
145
+ this.entries.clear();
146
+ this.inFlight.clear();
147
+ this.generations.clear();
148
+ this.subscribers.clear();
149
+ }
150
+ snapshot() {
151
+ const result = {};
152
+ for (const [key, entry] of this.entries) {
153
+ if (entry.status === "success") result[key] = entry;
154
+ }
155
+ return result;
156
+ }
157
+ restore(state) {
158
+ for (const [key, entry] of Object.entries(state)) {
159
+ if (!this.entries.has(key)) this.entries.set(key, entry);
160
+ }
161
+ }
162
+ };
163
+ var QueryClient = class {
164
+ constructor(config) {
165
+ this.config = config ?? {};
166
+ this.cache = new QueryCache(config?.gcTime);
167
+ }
168
+ getQueryData(key) {
169
+ return this.cache.get(serializeKey(key))?.data;
170
+ }
171
+ setQueryData(key, updater) {
172
+ const k = serializeKey(key);
173
+ const existing = this.cache.get(k);
174
+ const newData = typeof updater === "function" ? updater(existing?.data) : updater;
175
+ this.cache.setData(k, newData, existing?.staleTime ?? 6e4);
176
+ this.config.onSuccess?.(newData, k);
177
+ }
178
+ invalidateQueries(options) {
179
+ this.cache.markStale(serializeKey(options.queryKey));
180
+ }
181
+ async cancelQueries(options) {
182
+ const k = serializeKey(options.queryKey);
183
+ this.cache.getAbortController(k)?.abort();
184
+ this.cache.clearAbortController(k);
185
+ this.cache.bumpGeneration(k);
186
+ this.cache.clearInFlight(k);
187
+ this.cache.markStale(k);
188
+ }
189
+ async prefetchQuery(options) {
190
+ const k = serializeKey(options.queryKey);
191
+ if (!this.cache.isStale(k)) return;
192
+ try {
193
+ const data = await options.queryFn();
194
+ this.cache.setData(k, data, options.staleTime ?? 6e4);
195
+ this.config.onSuccess?.(data, k);
196
+ } catch {
197
+ }
198
+ }
199
+ // Called internally after setData / setError to fire global callbacks
200
+ notifySuccess(data, key) {
201
+ this.config.onSuccess?.(data, key);
202
+ }
203
+ notifyError(error, key) {
204
+ this.config.onError?.(error, key);
205
+ }
206
+ };
207
+
208
+ // src/server.ts
209
+ function dehydrate(queryClient) {
210
+ return { entries: queryClient.cache.snapshot() };
211
+ }
212
+ function hydrate(queryClient, state) {
213
+ queryClient.cache.restore(state.entries);
214
+ }
215
+ async function prefetchApiQuery(queryClient, apiClient, keyOrUrl, urlOrFn, options) {
216
+ const queryKey = typeof keyOrUrl === "string" ? [keyOrUrl] : keyOrUrl;
217
+ const queryFn = typeof urlOrFn === "function" ? () => urlOrFn(apiClient.instance) : typeof urlOrFn === "string" ? () => apiClient.instance.get(urlOrFn).then((r) => r.data) : () => apiClient.instance.get(keyOrUrl).then((r) => r.data);
218
+ await queryClient.prefetchQuery({
219
+ queryKey,
220
+ queryFn,
221
+ staleTime: options?.staleTime ?? 6e4
222
+ });
223
+ }
224
+
225
+ exports.QueryClient = QueryClient;
226
+ exports.dehydrate = dehydrate;
227
+ exports.hydrate = hydrate;
228
+ exports.prefetchApiQuery = prefetchApiQuery;
229
+ //# sourceMappingURL=server.cjs.map
230
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cache.ts","../src/server.ts"],"names":[],"mappings":"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","file":"server.cjs","sourcesContent":["import type { ApiError, QueryStatus, QueryClientConfig } from './types';\n\nexport type { QueryStatus };\n\nexport interface CacheEntry<TData = unknown> {\n data: TData | undefined;\n error: ApiError | undefined;\n status: QueryStatus;\n updatedAt: number;\n staleTime: number;\n}\n\ntype Subscriber = () => void;\n\nexport function serializeKey(key: unknown[]): string {\n return JSON.stringify(key);\n}\n\nexport class QueryCache {\n private entries = new Map<string, CacheEntry>();\n private subscribers = new Map<string, Set<Subscriber>>();\n private inFlight = new Map<string, Promise<unknown>>();\n // Discards responses from requests that were superseded (e.g. after cancelQueries)\n private generations = new Map<string, number>();\n // Evicts entries when no subscriber remains for longer than gcTime\n private gcTimers = new Map<string, ReturnType<typeof setTimeout>>();\n // AbortControllers for in-flight requests\n private abortControllers = new Map<string, AbortController>();\n // Global subscribers (for persistence and devtools)\n private globalSubscribers = new Set<() => void>();\n\n readonly gcTime: number;\n\n constructor(gcTime = 5 * 60_000) {\n this.gcTime = gcTime;\n }\n\n get<TData>(key: string): CacheEntry<TData> | undefined {\n return this.entries.get(key) as CacheEntry<TData> | undefined;\n }\n\n subscribe(key: string, fn: Subscriber): () => void {\n // A new subscriber cancels any pending GC eviction for this key\n const pending = this.gcTimers.get(key);\n if (pending !== undefined) {\n clearTimeout(pending);\n this.gcTimers.delete(key);\n }\n\n if (!this.subscribers.has(key)) this.subscribers.set(key, new Set());\n this.subscribers.get(key)!.add(fn);\n\n return () => {\n const set = this.subscribers.get(key);\n if (!set) return;\n set.delete(fn);\n if (set.size === 0) {\n this.subscribers.delete(key);\n // Schedule eviction after the last subscriber leaves\n const timer = setTimeout(() => {\n this.entries.delete(key);\n this.generations.delete(key);\n this.gcTimers.delete(key);\n }, this.gcTime);\n this.gcTimers.set(key, timer);\n }\n };\n }\n\n subscribeGlobal(fn: () => void): () => void {\n this.globalSubscribers.add(fn);\n return () => this.globalSubscribers.delete(fn);\n }\n\n private notify(key: string): void {\n this.subscribers.get(key)?.forEach((fn) => fn());\n this.globalSubscribers.forEach((fn) => fn());\n }\n\n setData<TData>(key: string, data: TData, staleTime: number): void {\n this.entries.set(key, {\n data,\n error: undefined,\n status: 'success',\n updatedAt: Date.now(),\n staleTime,\n });\n this.inFlight.delete(key);\n this.notify(key);\n }\n\n setError(key: string, error: ApiError): void {\n const existing = this.entries.get(key);\n this.entries.set(key, {\n data: existing?.data,\n error,\n status: 'error',\n updatedAt: Date.now(),\n staleTime: 0,\n });\n this.inFlight.delete(key);\n this.notify(key);\n }\n\n setLoading(key: string): void {\n const existing = this.entries.get(key);\n if (existing?.status === 'loading') return;\n this.entries.set(key, {\n data: existing?.data,\n error: undefined,\n status: 'loading',\n updatedAt: existing?.updatedAt ?? 0,\n staleTime: existing?.staleTime ?? 0,\n });\n this.notify(key);\n }\n\n isStale(key: string): boolean {\n const e = this.entries.get(key);\n if (!e || e.status === 'idle' || e.status === 'error') return true;\n if (e.status === 'loading') return false;\n // updatedAt === 0 is the sentinel written by markStale()\n return e.updatedAt === 0 || Date.now() - e.updatedAt > e.staleTime;\n }\n\n // True only when markStale() was explicitly called on a successful entry.\n // Used to detect *external* invalidation (e.g. after a mutation) vs time-based\n // staleness, so the re-fetch effect doesn't loop when staleTime is 0.\n isExternallyInvalidated(key: string): boolean {\n const e = this.entries.get(key);\n return !!e && e.status === 'success' && e.updatedAt === 0;\n }\n\n getInFlight(key: string): Promise<unknown> | undefined {\n return this.inFlight.get(key);\n }\n\n setInFlight(key: string, p: Promise<unknown>): void {\n this.inFlight.set(key, p);\n }\n\n clearInFlight(key: string): void {\n this.inFlight.delete(key);\n }\n\n // Returns the new generation. Does NOT touch inFlight — call clearInFlight separately.\n bumpGeneration(key: string): number {\n const g = (this.generations.get(key) ?? 0) + 1;\n this.generations.set(key, g);\n return g;\n }\n\n getGeneration(key: string): number {\n return this.generations.get(key) ?? 0;\n }\n\n // Sets updatedAt = 0 as the sentinel for \"externally invalidated\"\n markStale(key: string): void {\n const e = this.entries.get(key);\n if (e) {\n this.entries.set(key, { ...e, updatedAt: 0 });\n this.notify(key);\n }\n }\n\n // AbortController management\n setAbortController(key: string, ctrl: AbortController): void {\n this.abortControllers.set(key, ctrl);\n }\n\n getAbortController(key: string): AbortController | undefined {\n return this.abortControllers.get(key);\n }\n\n clearAbortController(key: string): void {\n this.abortControllers.delete(key);\n }\n\n clear(): void {\n for (const timer of this.gcTimers.values()) clearTimeout(timer);\n this.gcTimers.clear();\n // Abort all in-flight requests\n for (const ctrl of this.abortControllers.values()) ctrl.abort();\n this.abortControllers.clear();\n this.entries.clear();\n this.inFlight.clear();\n this.generations.clear();\n this.subscribers.clear();\n // globalSubscribers intentionally NOT cleared — they survive cache clears\n }\n\n snapshot(): Record<string, CacheEntry> {\n const result: Record<string, CacheEntry> = {};\n for (const [key, entry] of this.entries) {\n if (entry.status === 'success') result[key] = entry;\n }\n return result;\n }\n\n restore(state: Record<string, CacheEntry>): void {\n for (const [key, entry] of Object.entries(state)) {\n if (!this.entries.has(key)) this.entries.set(key, entry);\n }\n }\n}\n\nexport class QueryClient {\n readonly cache: QueryCache;\n private readonly config: QueryClientConfig;\n\n constructor(config?: QueryClientConfig) {\n this.config = config ?? {};\n this.cache = new QueryCache(config?.gcTime);\n }\n\n getQueryData<TData>(key: unknown[]): TData | undefined {\n return this.cache.get<TData>(serializeKey(key))?.data;\n }\n\n setQueryData<TData>(\n key: unknown[],\n updater: TData | ((old: TData | undefined) => TData),\n ): void {\n const k = serializeKey(key);\n const existing = this.cache.get<TData>(k);\n const newData =\n typeof updater === 'function'\n ? (updater as (old: TData | undefined) => TData)(existing?.data)\n : updater;\n this.cache.setData(k, newData, existing?.staleTime ?? 60_000);\n this.config.onSuccess?.(newData, k);\n }\n\n invalidateQueries(options: { queryKey: unknown[] }): void {\n this.cache.markStale(serializeKey(options.queryKey));\n }\n\n async cancelQueries(options: { queryKey: unknown[] }): Promise<void> {\n const k = serializeKey(options.queryKey);\n this.cache.getAbortController(k)?.abort();\n this.cache.clearAbortController(k);\n this.cache.bumpGeneration(k);\n this.cache.clearInFlight(k);\n this.cache.markStale(k);\n }\n\n async prefetchQuery<TData>(options: {\n queryKey: unknown[];\n queryFn: () => Promise<TData>;\n staleTime?: number;\n }): Promise<void> {\n const k = serializeKey(options.queryKey);\n if (!this.cache.isStale(k)) return;\n try {\n const data = await options.queryFn();\n this.cache.setData(k, data, options.staleTime ?? 60_000);\n this.config.onSuccess?.(data, k);\n } catch {\n // Prefetch failures are silent; the client will re-fetch\n }\n }\n\n // Called internally after setData / setError to fire global callbacks\n notifySuccess(data: unknown, key: string): void {\n this.config.onSuccess?.(data, key);\n }\n\n notifyError(error: ApiError, key: string): void {\n this.config.onError?.(error, key);\n }\n}\n","import type { AxiosInstance } from 'axios';\nimport { QueryClient } from './cache';\nimport type { CacheEntry } from './cache';\nimport type { ApiClient } from './client';\n\nexport interface DehydratedState {\n entries: Record<string, CacheEntry>;\n}\n\nexport interface PrefetchApiQueryOptions {\n staleTime?: number;\n}\n\n/** Serialize successful cache entries for SSR hydration. */\nexport function dehydrate(queryClient: QueryClient): DehydratedState {\n return { entries: queryClient.cache.snapshot() };\n}\n\n/** Restore a dehydrated state into a QueryClient. */\nexport function hydrate(queryClient: QueryClient, state: DehydratedState): void {\n queryClient.cache.restore(state.entries);\n}\n\n/**\n * Prefetch a query on the server (Next.js App Router, Pages Router, Remix loaders).\n * After prefetching, call `dehydrate(queryClient)` and pass the result to\n * `<ApiProvider hydratedState={...}>` on the client to avoid a loading flicker.\n */\nexport async function prefetchApiQuery<TData = unknown>(\n queryClient: QueryClient,\n apiClient: ApiClient,\n keyOrUrl: string | unknown[],\n urlOrFn?: string | ((instance: AxiosInstance) => Promise<TData>),\n options?: PrefetchApiQueryOptions,\n): Promise<void> {\n const queryKey = typeof keyOrUrl === 'string' ? [keyOrUrl] : keyOrUrl;\n\n const queryFn =\n typeof urlOrFn === 'function'\n ? () => urlOrFn(apiClient.instance)\n : typeof urlOrFn === 'string'\n ? () => apiClient.instance.get<TData>(urlOrFn).then((r) => r.data)\n : () => apiClient.instance.get<TData>(keyOrUrl as string).then((r) => r.data);\n\n await queryClient.prefetchQuery({\n queryKey,\n queryFn,\n staleTime: options?.staleTime ?? 60_000,\n });\n}\n\n// Re-exported so server files only need one import\nexport { QueryClient };\n"]}
@@ -0,0 +1,2 @@
1
+ import 'axios';
2
+ export { D as DehydratedState, h as PrefetchApiQueryOptions, Q as QueryClient, n as dehydrate, o as hydrate, p as prefetchApiQuery } from './server-BdrIj1E3.cjs';
@@ -0,0 +1,2 @@
1
+ import 'axios';
2
+ export { D as DehydratedState, h as PrefetchApiQueryOptions, Q as QueryClient, n as dehydrate, o as hydrate, p as prefetchApiQuery } from './server-BdrIj1E3.js';
@@ -0,0 +1,225 @@
1
+ // src/cache.ts
2
+ function serializeKey(key) {
3
+ return JSON.stringify(key);
4
+ }
5
+ var QueryCache = class {
6
+ constructor(gcTime = 5 * 6e4) {
7
+ this.entries = /* @__PURE__ */ new Map();
8
+ this.subscribers = /* @__PURE__ */ new Map();
9
+ this.inFlight = /* @__PURE__ */ new Map();
10
+ // Discards responses from requests that were superseded (e.g. after cancelQueries)
11
+ this.generations = /* @__PURE__ */ new Map();
12
+ // Evicts entries when no subscriber remains for longer than gcTime
13
+ this.gcTimers = /* @__PURE__ */ new Map();
14
+ // AbortControllers for in-flight requests
15
+ this.abortControllers = /* @__PURE__ */ new Map();
16
+ // Global subscribers (for persistence and devtools)
17
+ this.globalSubscribers = /* @__PURE__ */ new Set();
18
+ this.gcTime = gcTime;
19
+ }
20
+ get(key) {
21
+ return this.entries.get(key);
22
+ }
23
+ subscribe(key, fn) {
24
+ const pending = this.gcTimers.get(key);
25
+ if (pending !== void 0) {
26
+ clearTimeout(pending);
27
+ this.gcTimers.delete(key);
28
+ }
29
+ if (!this.subscribers.has(key)) this.subscribers.set(key, /* @__PURE__ */ new Set());
30
+ this.subscribers.get(key).add(fn);
31
+ return () => {
32
+ const set = this.subscribers.get(key);
33
+ if (!set) return;
34
+ set.delete(fn);
35
+ if (set.size === 0) {
36
+ this.subscribers.delete(key);
37
+ const timer = setTimeout(() => {
38
+ this.entries.delete(key);
39
+ this.generations.delete(key);
40
+ this.gcTimers.delete(key);
41
+ }, this.gcTime);
42
+ this.gcTimers.set(key, timer);
43
+ }
44
+ };
45
+ }
46
+ subscribeGlobal(fn) {
47
+ this.globalSubscribers.add(fn);
48
+ return () => this.globalSubscribers.delete(fn);
49
+ }
50
+ notify(key) {
51
+ this.subscribers.get(key)?.forEach((fn) => fn());
52
+ this.globalSubscribers.forEach((fn) => fn());
53
+ }
54
+ setData(key, data, staleTime) {
55
+ this.entries.set(key, {
56
+ data,
57
+ error: void 0,
58
+ status: "success",
59
+ updatedAt: Date.now(),
60
+ staleTime
61
+ });
62
+ this.inFlight.delete(key);
63
+ this.notify(key);
64
+ }
65
+ setError(key, error) {
66
+ const existing = this.entries.get(key);
67
+ this.entries.set(key, {
68
+ data: existing?.data,
69
+ error,
70
+ status: "error",
71
+ updatedAt: Date.now(),
72
+ staleTime: 0
73
+ });
74
+ this.inFlight.delete(key);
75
+ this.notify(key);
76
+ }
77
+ setLoading(key) {
78
+ const existing = this.entries.get(key);
79
+ if (existing?.status === "loading") return;
80
+ this.entries.set(key, {
81
+ data: existing?.data,
82
+ error: void 0,
83
+ status: "loading",
84
+ updatedAt: existing?.updatedAt ?? 0,
85
+ staleTime: existing?.staleTime ?? 0
86
+ });
87
+ this.notify(key);
88
+ }
89
+ isStale(key) {
90
+ const e = this.entries.get(key);
91
+ if (!e || e.status === "idle" || e.status === "error") return true;
92
+ if (e.status === "loading") return false;
93
+ return e.updatedAt === 0 || Date.now() - e.updatedAt > e.staleTime;
94
+ }
95
+ // True only when markStale() was explicitly called on a successful entry.
96
+ // Used to detect *external* invalidation (e.g. after a mutation) vs time-based
97
+ // staleness, so the re-fetch effect doesn't loop when staleTime is 0.
98
+ isExternallyInvalidated(key) {
99
+ const e = this.entries.get(key);
100
+ return !!e && e.status === "success" && e.updatedAt === 0;
101
+ }
102
+ getInFlight(key) {
103
+ return this.inFlight.get(key);
104
+ }
105
+ setInFlight(key, p) {
106
+ this.inFlight.set(key, p);
107
+ }
108
+ clearInFlight(key) {
109
+ this.inFlight.delete(key);
110
+ }
111
+ // Returns the new generation. Does NOT touch inFlight — call clearInFlight separately.
112
+ bumpGeneration(key) {
113
+ const g = (this.generations.get(key) ?? 0) + 1;
114
+ this.generations.set(key, g);
115
+ return g;
116
+ }
117
+ getGeneration(key) {
118
+ return this.generations.get(key) ?? 0;
119
+ }
120
+ // Sets updatedAt = 0 as the sentinel for "externally invalidated"
121
+ markStale(key) {
122
+ const e = this.entries.get(key);
123
+ if (e) {
124
+ this.entries.set(key, { ...e, updatedAt: 0 });
125
+ this.notify(key);
126
+ }
127
+ }
128
+ // AbortController management
129
+ setAbortController(key, ctrl) {
130
+ this.abortControllers.set(key, ctrl);
131
+ }
132
+ getAbortController(key) {
133
+ return this.abortControllers.get(key);
134
+ }
135
+ clearAbortController(key) {
136
+ this.abortControllers.delete(key);
137
+ }
138
+ clear() {
139
+ for (const timer of this.gcTimers.values()) clearTimeout(timer);
140
+ this.gcTimers.clear();
141
+ for (const ctrl of this.abortControllers.values()) ctrl.abort();
142
+ this.abortControllers.clear();
143
+ this.entries.clear();
144
+ this.inFlight.clear();
145
+ this.generations.clear();
146
+ this.subscribers.clear();
147
+ }
148
+ snapshot() {
149
+ const result = {};
150
+ for (const [key, entry] of this.entries) {
151
+ if (entry.status === "success") result[key] = entry;
152
+ }
153
+ return result;
154
+ }
155
+ restore(state) {
156
+ for (const [key, entry] of Object.entries(state)) {
157
+ if (!this.entries.has(key)) this.entries.set(key, entry);
158
+ }
159
+ }
160
+ };
161
+ var QueryClient = class {
162
+ constructor(config) {
163
+ this.config = config ?? {};
164
+ this.cache = new QueryCache(config?.gcTime);
165
+ }
166
+ getQueryData(key) {
167
+ return this.cache.get(serializeKey(key))?.data;
168
+ }
169
+ setQueryData(key, updater) {
170
+ const k = serializeKey(key);
171
+ const existing = this.cache.get(k);
172
+ const newData = typeof updater === "function" ? updater(existing?.data) : updater;
173
+ this.cache.setData(k, newData, existing?.staleTime ?? 6e4);
174
+ this.config.onSuccess?.(newData, k);
175
+ }
176
+ invalidateQueries(options) {
177
+ this.cache.markStale(serializeKey(options.queryKey));
178
+ }
179
+ async cancelQueries(options) {
180
+ const k = serializeKey(options.queryKey);
181
+ this.cache.getAbortController(k)?.abort();
182
+ this.cache.clearAbortController(k);
183
+ this.cache.bumpGeneration(k);
184
+ this.cache.clearInFlight(k);
185
+ this.cache.markStale(k);
186
+ }
187
+ async prefetchQuery(options) {
188
+ const k = serializeKey(options.queryKey);
189
+ if (!this.cache.isStale(k)) return;
190
+ try {
191
+ const data = await options.queryFn();
192
+ this.cache.setData(k, data, options.staleTime ?? 6e4);
193
+ this.config.onSuccess?.(data, k);
194
+ } catch {
195
+ }
196
+ }
197
+ // Called internally after setData / setError to fire global callbacks
198
+ notifySuccess(data, key) {
199
+ this.config.onSuccess?.(data, key);
200
+ }
201
+ notifyError(error, key) {
202
+ this.config.onError?.(error, key);
203
+ }
204
+ };
205
+
206
+ // src/server.ts
207
+ function dehydrate(queryClient) {
208
+ return { entries: queryClient.cache.snapshot() };
209
+ }
210
+ function hydrate(queryClient, state) {
211
+ queryClient.cache.restore(state.entries);
212
+ }
213
+ async function prefetchApiQuery(queryClient, apiClient, keyOrUrl, urlOrFn, options) {
214
+ const queryKey = typeof keyOrUrl === "string" ? [keyOrUrl] : keyOrUrl;
215
+ const queryFn = typeof urlOrFn === "function" ? () => urlOrFn(apiClient.instance) : typeof urlOrFn === "string" ? () => apiClient.instance.get(urlOrFn).then((r) => r.data) : () => apiClient.instance.get(keyOrUrl).then((r) => r.data);
216
+ await queryClient.prefetchQuery({
217
+ queryKey,
218
+ queryFn,
219
+ staleTime: options?.staleTime ?? 6e4
220
+ });
221
+ }
222
+
223
+ export { QueryClient, dehydrate, hydrate, prefetchApiQuery };
224
+ //# sourceMappingURL=server.mjs.map
225
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cache.ts","../src/server.ts"],"names":[],"mappings":"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","file":"server.mjs","sourcesContent":["import type { ApiError, QueryStatus, QueryClientConfig } from './types';\n\nexport type { QueryStatus };\n\nexport interface CacheEntry<TData = unknown> {\n data: TData | undefined;\n error: ApiError | undefined;\n status: QueryStatus;\n updatedAt: number;\n staleTime: number;\n}\n\ntype Subscriber = () => void;\n\nexport function serializeKey(key: unknown[]): string {\n return JSON.stringify(key);\n}\n\nexport class QueryCache {\n private entries = new Map<string, CacheEntry>();\n private subscribers = new Map<string, Set<Subscriber>>();\n private inFlight = new Map<string, Promise<unknown>>();\n // Discards responses from requests that were superseded (e.g. after cancelQueries)\n private generations = new Map<string, number>();\n // Evicts entries when no subscriber remains for longer than gcTime\n private gcTimers = new Map<string, ReturnType<typeof setTimeout>>();\n // AbortControllers for in-flight requests\n private abortControllers = new Map<string, AbortController>();\n // Global subscribers (for persistence and devtools)\n private globalSubscribers = new Set<() => void>();\n\n readonly gcTime: number;\n\n constructor(gcTime = 5 * 60_000) {\n this.gcTime = gcTime;\n }\n\n get<TData>(key: string): CacheEntry<TData> | undefined {\n return this.entries.get(key) as CacheEntry<TData> | undefined;\n }\n\n subscribe(key: string, fn: Subscriber): () => void {\n // A new subscriber cancels any pending GC eviction for this key\n const pending = this.gcTimers.get(key);\n if (pending !== undefined) {\n clearTimeout(pending);\n this.gcTimers.delete(key);\n }\n\n if (!this.subscribers.has(key)) this.subscribers.set(key, new Set());\n this.subscribers.get(key)!.add(fn);\n\n return () => {\n const set = this.subscribers.get(key);\n if (!set) return;\n set.delete(fn);\n if (set.size === 0) {\n this.subscribers.delete(key);\n // Schedule eviction after the last subscriber leaves\n const timer = setTimeout(() => {\n this.entries.delete(key);\n this.generations.delete(key);\n this.gcTimers.delete(key);\n }, this.gcTime);\n this.gcTimers.set(key, timer);\n }\n };\n }\n\n subscribeGlobal(fn: () => void): () => void {\n this.globalSubscribers.add(fn);\n return () => this.globalSubscribers.delete(fn);\n }\n\n private notify(key: string): void {\n this.subscribers.get(key)?.forEach((fn) => fn());\n this.globalSubscribers.forEach((fn) => fn());\n }\n\n setData<TData>(key: string, data: TData, staleTime: number): void {\n this.entries.set(key, {\n data,\n error: undefined,\n status: 'success',\n updatedAt: Date.now(),\n staleTime,\n });\n this.inFlight.delete(key);\n this.notify(key);\n }\n\n setError(key: string, error: ApiError): void {\n const existing = this.entries.get(key);\n this.entries.set(key, {\n data: existing?.data,\n error,\n status: 'error',\n updatedAt: Date.now(),\n staleTime: 0,\n });\n this.inFlight.delete(key);\n this.notify(key);\n }\n\n setLoading(key: string): void {\n const existing = this.entries.get(key);\n if (existing?.status === 'loading') return;\n this.entries.set(key, {\n data: existing?.data,\n error: undefined,\n status: 'loading',\n updatedAt: existing?.updatedAt ?? 0,\n staleTime: existing?.staleTime ?? 0,\n });\n this.notify(key);\n }\n\n isStale(key: string): boolean {\n const e = this.entries.get(key);\n if (!e || e.status === 'idle' || e.status === 'error') return true;\n if (e.status === 'loading') return false;\n // updatedAt === 0 is the sentinel written by markStale()\n return e.updatedAt === 0 || Date.now() - e.updatedAt > e.staleTime;\n }\n\n // True only when markStale() was explicitly called on a successful entry.\n // Used to detect *external* invalidation (e.g. after a mutation) vs time-based\n // staleness, so the re-fetch effect doesn't loop when staleTime is 0.\n isExternallyInvalidated(key: string): boolean {\n const e = this.entries.get(key);\n return !!e && e.status === 'success' && e.updatedAt === 0;\n }\n\n getInFlight(key: string): Promise<unknown> | undefined {\n return this.inFlight.get(key);\n }\n\n setInFlight(key: string, p: Promise<unknown>): void {\n this.inFlight.set(key, p);\n }\n\n clearInFlight(key: string): void {\n this.inFlight.delete(key);\n }\n\n // Returns the new generation. Does NOT touch inFlight — call clearInFlight separately.\n bumpGeneration(key: string): number {\n const g = (this.generations.get(key) ?? 0) + 1;\n this.generations.set(key, g);\n return g;\n }\n\n getGeneration(key: string): number {\n return this.generations.get(key) ?? 0;\n }\n\n // Sets updatedAt = 0 as the sentinel for \"externally invalidated\"\n markStale(key: string): void {\n const e = this.entries.get(key);\n if (e) {\n this.entries.set(key, { ...e, updatedAt: 0 });\n this.notify(key);\n }\n }\n\n // AbortController management\n setAbortController(key: string, ctrl: AbortController): void {\n this.abortControllers.set(key, ctrl);\n }\n\n getAbortController(key: string): AbortController | undefined {\n return this.abortControllers.get(key);\n }\n\n clearAbortController(key: string): void {\n this.abortControllers.delete(key);\n }\n\n clear(): void {\n for (const timer of this.gcTimers.values()) clearTimeout(timer);\n this.gcTimers.clear();\n // Abort all in-flight requests\n for (const ctrl of this.abortControllers.values()) ctrl.abort();\n this.abortControllers.clear();\n this.entries.clear();\n this.inFlight.clear();\n this.generations.clear();\n this.subscribers.clear();\n // globalSubscribers intentionally NOT cleared — they survive cache clears\n }\n\n snapshot(): Record<string, CacheEntry> {\n const result: Record<string, CacheEntry> = {};\n for (const [key, entry] of this.entries) {\n if (entry.status === 'success') result[key] = entry;\n }\n return result;\n }\n\n restore(state: Record<string, CacheEntry>): void {\n for (const [key, entry] of Object.entries(state)) {\n if (!this.entries.has(key)) this.entries.set(key, entry);\n }\n }\n}\n\nexport class QueryClient {\n readonly cache: QueryCache;\n private readonly config: QueryClientConfig;\n\n constructor(config?: QueryClientConfig) {\n this.config = config ?? {};\n this.cache = new QueryCache(config?.gcTime);\n }\n\n getQueryData<TData>(key: unknown[]): TData | undefined {\n return this.cache.get<TData>(serializeKey(key))?.data;\n }\n\n setQueryData<TData>(\n key: unknown[],\n updater: TData | ((old: TData | undefined) => TData),\n ): void {\n const k = serializeKey(key);\n const existing = this.cache.get<TData>(k);\n const newData =\n typeof updater === 'function'\n ? (updater as (old: TData | undefined) => TData)(existing?.data)\n : updater;\n this.cache.setData(k, newData, existing?.staleTime ?? 60_000);\n this.config.onSuccess?.(newData, k);\n }\n\n invalidateQueries(options: { queryKey: unknown[] }): void {\n this.cache.markStale(serializeKey(options.queryKey));\n }\n\n async cancelQueries(options: { queryKey: unknown[] }): Promise<void> {\n const k = serializeKey(options.queryKey);\n this.cache.getAbortController(k)?.abort();\n this.cache.clearAbortController(k);\n this.cache.bumpGeneration(k);\n this.cache.clearInFlight(k);\n this.cache.markStale(k);\n }\n\n async prefetchQuery<TData>(options: {\n queryKey: unknown[];\n queryFn: () => Promise<TData>;\n staleTime?: number;\n }): Promise<void> {\n const k = serializeKey(options.queryKey);\n if (!this.cache.isStale(k)) return;\n try {\n const data = await options.queryFn();\n this.cache.setData(k, data, options.staleTime ?? 60_000);\n this.config.onSuccess?.(data, k);\n } catch {\n // Prefetch failures are silent; the client will re-fetch\n }\n }\n\n // Called internally after setData / setError to fire global callbacks\n notifySuccess(data: unknown, key: string): void {\n this.config.onSuccess?.(data, key);\n }\n\n notifyError(error: ApiError, key: string): void {\n this.config.onError?.(error, key);\n }\n}\n","import type { AxiosInstance } from 'axios';\nimport { QueryClient } from './cache';\nimport type { CacheEntry } from './cache';\nimport type { ApiClient } from './client';\n\nexport interface DehydratedState {\n entries: Record<string, CacheEntry>;\n}\n\nexport interface PrefetchApiQueryOptions {\n staleTime?: number;\n}\n\n/** Serialize successful cache entries for SSR hydration. */\nexport function dehydrate(queryClient: QueryClient): DehydratedState {\n return { entries: queryClient.cache.snapshot() };\n}\n\n/** Restore a dehydrated state into a QueryClient. */\nexport function hydrate(queryClient: QueryClient, state: DehydratedState): void {\n queryClient.cache.restore(state.entries);\n}\n\n/**\n * Prefetch a query on the server (Next.js App Router, Pages Router, Remix loaders).\n * After prefetching, call `dehydrate(queryClient)` and pass the result to\n * `<ApiProvider hydratedState={...}>` on the client to avoid a loading flicker.\n */\nexport async function prefetchApiQuery<TData = unknown>(\n queryClient: QueryClient,\n apiClient: ApiClient,\n keyOrUrl: string | unknown[],\n urlOrFn?: string | ((instance: AxiosInstance) => Promise<TData>),\n options?: PrefetchApiQueryOptions,\n): Promise<void> {\n const queryKey = typeof keyOrUrl === 'string' ? [keyOrUrl] : keyOrUrl;\n\n const queryFn =\n typeof urlOrFn === 'function'\n ? () => urlOrFn(apiClient.instance)\n : typeof urlOrFn === 'string'\n ? () => apiClient.instance.get<TData>(urlOrFn).then((r) => r.data)\n : () => apiClient.instance.get<TData>(keyOrUrl as string).then((r) => r.data);\n\n await queryClient.prefetchQuery({\n queryKey,\n queryFn,\n staleTime: options?.staleTime ?? 60_000,\n });\n}\n\n// Re-exported so server files only need one import\nexport { QueryClient };\n"]}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@structyl/api-client",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "React data-fetching for any framework: Axios client with a built-in query cache, hooks for queries, mutations, infinite & suspense, plus SSR.",
8
+ "keywords": [
9
+ "react",
10
+ "typescript",
11
+ "structyl",
12
+ "axios",
13
+ "data-fetching",
14
+ "react-query",
15
+ "cache",
16
+ "hooks",
17
+ "mutations",
18
+ "infinite-query",
19
+ "suspense",
20
+ "ssr"
21
+ ],
22
+ "license": "MIT",
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "main": "./dist/index.cjs",
26
+ "module": "./dist/index.mjs",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.mjs",
32
+ "require": "./dist/index.cjs"
33
+ },
34
+ "./server": {
35
+ "types": "./dist/server.d.ts",
36
+ "import": "./dist/server.mjs",
37
+ "require": "./dist/server.cjs"
38
+ },
39
+ "./devtools": {
40
+ "types": "./dist/devtools.d.ts",
41
+ "import": "./dist/devtools.mjs",
42
+ "require": "./dist/devtools.cjs"
43
+ }
44
+ },
45
+ "files": [
46
+ "dist"
47
+ ],
48
+ "peerDependencies": {
49
+ "axios": "^1.0.0",
50
+ "react": "^18.0.0 || ^19.0.0"
51
+ },
52
+ "devDependencies": {
53
+ "@testing-library/react": "^16.0.0",
54
+ "@types/react": "^19.0.0",
55
+ "axios": "^1.7.7",
56
+ "axios-mock-adapter": "^2.1.0",
57
+ "react": "^19.0.0",
58
+ "react-dom": "^19.0.0",
59
+ "tsup": "^8.3.0",
60
+ "typescript": "^5.6.3",
61
+ "vitest": "^3.2.4"
62
+ },
63
+ "scripts": {
64
+ "build": "tsup",
65
+ "dev": "tsup --watch",
66
+ "lint": "eslint src",
67
+ "test": "vitest run",
68
+ "test:watch": "vitest",
69
+ "typecheck": "tsc --noEmit",
70
+ "clean": "rm -rf dist .turbo"
71
+ }
72
+ }