@flightdev/data 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +265 -0
- package/dist/cache-DU1v4CKe.d.ts +216 -0
- package/dist/chunk-NOWQL3EM.js +398 -0
- package/dist/index.d.ts +277 -0
- package/dist/index.js +463 -0
- package/dist/react.d.ts +57 -0
- package/dist/react.js +119 -0
- package/dist/solid.d.ts +83 -0
- package/dist/solid.js +142 -0
- package/dist/svelte.d.ts +80 -0
- package/dist/svelte.js +114 -0
- package/dist/vue.d.ts +76 -0
- package/dist/vue.js +136 -0
- package/package.json +84 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LRUCache,
|
|
3
|
+
asyncData,
|
|
4
|
+
cancelRequest,
|
|
5
|
+
clearCache,
|
|
6
|
+
configureCache,
|
|
7
|
+
createFetchHydrationScript,
|
|
8
|
+
fetchData,
|
|
9
|
+
getCache,
|
|
10
|
+
getCachedData,
|
|
11
|
+
hydrateFetchData,
|
|
12
|
+
invalidate,
|
|
13
|
+
invalidateCache,
|
|
14
|
+
isClient,
|
|
15
|
+
isServer,
|
|
16
|
+
prefetch,
|
|
17
|
+
setCachedData
|
|
18
|
+
} from "./chunk-NOWQL3EM.js";
|
|
19
|
+
|
|
20
|
+
// src/hooks.ts
|
|
21
|
+
var isBrowser = typeof window !== "undefined";
|
|
22
|
+
var loaderDataStore = /* @__PURE__ */ new Map();
|
|
23
|
+
var actionDataStore = /* @__PURE__ */ new Map();
|
|
24
|
+
function setLoaderData(key, data) {
|
|
25
|
+
loaderDataStore.set(key, data);
|
|
26
|
+
}
|
|
27
|
+
function getLoaderData(key) {
|
|
28
|
+
return loaderDataStore.get(key);
|
|
29
|
+
}
|
|
30
|
+
function setActionData(key, data) {
|
|
31
|
+
actionDataStore.set(key, data);
|
|
32
|
+
}
|
|
33
|
+
function getActionData(key) {
|
|
34
|
+
return actionDataStore.get(key);
|
|
35
|
+
}
|
|
36
|
+
function clearDataStores() {
|
|
37
|
+
loaderDataStore.clear();
|
|
38
|
+
actionDataStore.clear();
|
|
39
|
+
}
|
|
40
|
+
function getCurrentPathname() {
|
|
41
|
+
if (isBrowser) {
|
|
42
|
+
return window.location.pathname;
|
|
43
|
+
}
|
|
44
|
+
return "/";
|
|
45
|
+
}
|
|
46
|
+
function getHydratedLoaderData(pathname) {
|
|
47
|
+
if (!isBrowser) return void 0;
|
|
48
|
+
const hydrated = window.__FLIGHT_LOADER_DATA__;
|
|
49
|
+
if (!hydrated) return void 0;
|
|
50
|
+
const key = pathname ?? getCurrentPathname();
|
|
51
|
+
return hydrated[key];
|
|
52
|
+
}
|
|
53
|
+
function getHydratedActionData() {
|
|
54
|
+
if (!isBrowser) return void 0;
|
|
55
|
+
return window.__FLIGHT_ACTION_DATA__;
|
|
56
|
+
}
|
|
57
|
+
function use(promise) {
|
|
58
|
+
const thenable = promise;
|
|
59
|
+
if (thenable.status === "fulfilled") {
|
|
60
|
+
return thenable.value;
|
|
61
|
+
}
|
|
62
|
+
if (thenable.status === "rejected") {
|
|
63
|
+
throw thenable.reason;
|
|
64
|
+
}
|
|
65
|
+
if (thenable.status !== "pending") {
|
|
66
|
+
thenable.status = "pending";
|
|
67
|
+
thenable.then(
|
|
68
|
+
(value) => {
|
|
69
|
+
thenable.status = "fulfilled";
|
|
70
|
+
thenable.value = value;
|
|
71
|
+
},
|
|
72
|
+
(error) => {
|
|
73
|
+
thenable.status = "rejected";
|
|
74
|
+
thenable.reason = error instanceof Error ? error : new Error(String(error));
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
throw thenable;
|
|
79
|
+
}
|
|
80
|
+
var reactHooks = null;
|
|
81
|
+
function getReactHooks() {
|
|
82
|
+
if (reactHooks) return reactHooks;
|
|
83
|
+
let React;
|
|
84
|
+
if (typeof globalThis !== "undefined") {
|
|
85
|
+
const maybeReact = globalThis.React;
|
|
86
|
+
if (maybeReact && typeof maybeReact.useState === "function" && typeof maybeReact.useCallback === "function") {
|
|
87
|
+
React = maybeReact;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!React && isBrowser) {
|
|
91
|
+
const win = window;
|
|
92
|
+
if (win.React) {
|
|
93
|
+
const maybeReact = win.React;
|
|
94
|
+
if (maybeReact && typeof maybeReact.useState === "function" && typeof maybeReact.useCallback === "function") {
|
|
95
|
+
React = maybeReact;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!React) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const { useState, useCallback } = React;
|
|
103
|
+
reactHooks = {
|
|
104
|
+
/**
|
|
105
|
+
* useLoaderData - Access loader data from SSR or context
|
|
106
|
+
*/
|
|
107
|
+
useLoaderData: function useFlightLoaderData() {
|
|
108
|
+
const [data] = useState(() => {
|
|
109
|
+
const hydrated = getHydratedLoaderData();
|
|
110
|
+
if (hydrated !== void 0) return hydrated;
|
|
111
|
+
const stored = getLoaderData(getCurrentPathname());
|
|
112
|
+
if (stored !== void 0) return stored;
|
|
113
|
+
return void 0;
|
|
114
|
+
});
|
|
115
|
+
return data;
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* useActionData - Access action result from form submission
|
|
119
|
+
*/
|
|
120
|
+
useActionData: function useFlightActionData() {
|
|
121
|
+
const [data] = useState(() => {
|
|
122
|
+
const hydrated = getHydratedActionData();
|
|
123
|
+
if (hydrated !== void 0) return hydrated;
|
|
124
|
+
const stored = getActionData(getCurrentPathname());
|
|
125
|
+
if (stored !== void 0) return stored;
|
|
126
|
+
return void 0;
|
|
127
|
+
});
|
|
128
|
+
return data;
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* useFetcher - Client-side data fetching without navigation
|
|
132
|
+
*/
|
|
133
|
+
useFetcher: function useFlightFetcher() {
|
|
134
|
+
const [state, setState] = useState({
|
|
135
|
+
state: "idle",
|
|
136
|
+
data: void 0,
|
|
137
|
+
error: void 0
|
|
138
|
+
});
|
|
139
|
+
const reset = useCallback(() => {
|
|
140
|
+
setState({ state: "idle", data: void 0, error: void 0 });
|
|
141
|
+
}, []);
|
|
142
|
+
const submit = useCallback(async (formData, options = {}) => {
|
|
143
|
+
const { method = "POST", action = getCurrentPathname() } = options;
|
|
144
|
+
setState({ state: "loading", data: void 0, error: void 0 });
|
|
145
|
+
try {
|
|
146
|
+
const body = formData instanceof FormData ? formData : new URLSearchParams(formData);
|
|
147
|
+
const headers = {};
|
|
148
|
+
if (!(formData instanceof FormData)) {
|
|
149
|
+
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
150
|
+
}
|
|
151
|
+
const response = await fetch(action, {
|
|
152
|
+
method,
|
|
153
|
+
body,
|
|
154
|
+
headers: Object.keys(headers).length > 0 ? headers : void 0
|
|
155
|
+
});
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
|
|
158
|
+
}
|
|
159
|
+
const contentType = response.headers.get("Content-Type") ?? "";
|
|
160
|
+
let result;
|
|
161
|
+
if (contentType.includes("application/json")) {
|
|
162
|
+
result = await response.json();
|
|
163
|
+
} else {
|
|
164
|
+
result = await response.text();
|
|
165
|
+
}
|
|
166
|
+
setState({ state: "success", data: result, error: void 0 });
|
|
167
|
+
} catch (error) {
|
|
168
|
+
setState({
|
|
169
|
+
state: "error",
|
|
170
|
+
data: void 0,
|
|
171
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}, []);
|
|
175
|
+
const load = useCallback(async (href) => {
|
|
176
|
+
setState({ state: "loading", data: void 0, error: void 0 });
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetch(href, {
|
|
179
|
+
headers: { "Accept": "application/json" }
|
|
180
|
+
});
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
|
|
183
|
+
}
|
|
184
|
+
const contentType = response.headers.get("Content-Type") ?? "";
|
|
185
|
+
let result;
|
|
186
|
+
if (contentType.includes("application/json")) {
|
|
187
|
+
result = await response.json();
|
|
188
|
+
} else {
|
|
189
|
+
result = await response.text();
|
|
190
|
+
}
|
|
191
|
+
setState({ state: "success", data: result, error: void 0 });
|
|
192
|
+
} catch (error) {
|
|
193
|
+
setState({
|
|
194
|
+
state: "error",
|
|
195
|
+
data: void 0,
|
|
196
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}, []);
|
|
200
|
+
return {
|
|
201
|
+
state: state.state,
|
|
202
|
+
data: state.state === "success" ? state.data : void 0,
|
|
203
|
+
error: state.state === "error" ? state.error : void 0,
|
|
204
|
+
submit,
|
|
205
|
+
load,
|
|
206
|
+
reset
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
return reactHooks;
|
|
211
|
+
}
|
|
212
|
+
function useLoaderData() {
|
|
213
|
+
const hooks = getReactHooks();
|
|
214
|
+
if (hooks) {
|
|
215
|
+
return hooks.useLoaderData();
|
|
216
|
+
}
|
|
217
|
+
const hydrated = getHydratedLoaderData();
|
|
218
|
+
if (hydrated !== void 0) return hydrated;
|
|
219
|
+
return getLoaderData(getCurrentPathname());
|
|
220
|
+
}
|
|
221
|
+
function useActionData() {
|
|
222
|
+
const hooks = getReactHooks();
|
|
223
|
+
if (hooks) {
|
|
224
|
+
return hooks.useActionData();
|
|
225
|
+
}
|
|
226
|
+
const hydrated = getHydratedActionData();
|
|
227
|
+
if (hydrated !== void 0) return hydrated;
|
|
228
|
+
return getActionData(getCurrentPathname());
|
|
229
|
+
}
|
|
230
|
+
function useFetcher() {
|
|
231
|
+
const hooks = getReactHooks();
|
|
232
|
+
if (hooks) {
|
|
233
|
+
return hooks.useFetcher();
|
|
234
|
+
}
|
|
235
|
+
console.warn("[Flight] useFetcher called outside React context. Returning static implementation.");
|
|
236
|
+
return {
|
|
237
|
+
state: "idle",
|
|
238
|
+
data: void 0,
|
|
239
|
+
error: void 0,
|
|
240
|
+
submit: () => {
|
|
241
|
+
console.warn("[Flight] useFetcher.submit() called outside React context");
|
|
242
|
+
},
|
|
243
|
+
load: () => {
|
|
244
|
+
console.warn("[Flight] useFetcher.load() called outside React context");
|
|
245
|
+
},
|
|
246
|
+
reset: () => {
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function createHydrationScript(pathname, loaderData, actionData) {
|
|
251
|
+
const script = ["<script>"];
|
|
252
|
+
if (loaderData !== void 0) {
|
|
253
|
+
script.push(
|
|
254
|
+
`window.__FLIGHT_LOADER_DATA__ = window.__FLIGHT_LOADER_DATA__ || {};`,
|
|
255
|
+
`window.__FLIGHT_LOADER_DATA__[${JSON.stringify(pathname)}] = ${JSON.stringify(loaderData)};`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
if (actionData !== void 0) {
|
|
259
|
+
script.push(
|
|
260
|
+
`window.__FLIGHT_ACTION_DATA__ = ${JSON.stringify(actionData)};`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
script.push("</script>");
|
|
264
|
+
return script.join("\n");
|
|
265
|
+
}
|
|
266
|
+
function hydrateFromWindow() {
|
|
267
|
+
if (!isBrowser) return;
|
|
268
|
+
const loaderData = window.__FLIGHT_LOADER_DATA__;
|
|
269
|
+
if (loaderData) {
|
|
270
|
+
for (const [key, value] of Object.entries(loaderData)) {
|
|
271
|
+
setLoaderData(key, value);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const actionData = window.__FLIGHT_ACTION_DATA__;
|
|
275
|
+
if (actionData !== void 0) {
|
|
276
|
+
setActionData(getCurrentPathname(), actionData);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/form.ts
|
|
281
|
+
var isBrowser2 = typeof window !== "undefined";
|
|
282
|
+
var Form = null;
|
|
283
|
+
if (typeof globalThis !== "undefined") {
|
|
284
|
+
try {
|
|
285
|
+
const React = globalThis.React;
|
|
286
|
+
if (React?.createElement) {
|
|
287
|
+
const { useState, useCallback } = React;
|
|
288
|
+
Form = function FlightForm({
|
|
289
|
+
action,
|
|
290
|
+
method = "post",
|
|
291
|
+
children,
|
|
292
|
+
className,
|
|
293
|
+
onSuccess,
|
|
294
|
+
onError,
|
|
295
|
+
replace = false,
|
|
296
|
+
encType = "application/x-www-form-urlencoded"
|
|
297
|
+
}) {
|
|
298
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
299
|
+
const handleSubmit = useCallback(async (e) => {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
if (!isBrowser2) return;
|
|
302
|
+
const form = e.target;
|
|
303
|
+
const formData = new FormData(form);
|
|
304
|
+
const targetAction = action || form.action || window.location.pathname;
|
|
305
|
+
const targetMethod = method.toUpperCase();
|
|
306
|
+
setIsSubmitting(true);
|
|
307
|
+
try {
|
|
308
|
+
const response = await fetch(targetAction, {
|
|
309
|
+
method: targetMethod,
|
|
310
|
+
body: encType === "multipart/form-data" ? formData : new URLSearchParams(formData),
|
|
311
|
+
headers: encType === "multipart/form-data" ? void 0 : { "Content-Type": "application/x-www-form-urlencoded" }
|
|
312
|
+
});
|
|
313
|
+
if (response.redirected) {
|
|
314
|
+
const redirectUrl = response.url;
|
|
315
|
+
if (replace) {
|
|
316
|
+
window.history.replaceState(null, "", redirectUrl);
|
|
317
|
+
} else {
|
|
318
|
+
window.history.pushState(null, "", redirectUrl);
|
|
319
|
+
}
|
|
320
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
321
|
+
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const location = response.headers.get("Location");
|
|
325
|
+
if (location) {
|
|
326
|
+
if (replace) {
|
|
327
|
+
window.history.replaceState(null, "", location);
|
|
328
|
+
} else {
|
|
329
|
+
window.history.pushState(null, "", location);
|
|
330
|
+
}
|
|
331
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
332
|
+
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (!response.ok) {
|
|
336
|
+
throw new Error(`Form submission failed: ${response.status}`);
|
|
337
|
+
}
|
|
338
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
339
|
+
let data;
|
|
340
|
+
if (contentType.includes("application/json")) {
|
|
341
|
+
data = await response.json();
|
|
342
|
+
} else {
|
|
343
|
+
data = await response.text();
|
|
344
|
+
}
|
|
345
|
+
window.__FLIGHT_ACTION_DATA__ = data;
|
|
346
|
+
onSuccess?.(data);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
349
|
+
onError?.(err);
|
|
350
|
+
} finally {
|
|
351
|
+
setIsSubmitting(false);
|
|
352
|
+
}
|
|
353
|
+
}, [action, method, encType, replace, onSuccess, onError]);
|
|
354
|
+
return React.createElement("form", {
|
|
355
|
+
action,
|
|
356
|
+
method,
|
|
357
|
+
className,
|
|
358
|
+
encType,
|
|
359
|
+
onSubmit: handleSubmit,
|
|
360
|
+
"data-submitting": isSubmitting || void 0
|
|
361
|
+
}, children);
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/response.ts
|
|
369
|
+
function redirect(url, init) {
|
|
370
|
+
const headers = new Headers(init?.headers);
|
|
371
|
+
headers.set("Location", url);
|
|
372
|
+
return new Response(null, {
|
|
373
|
+
...init,
|
|
374
|
+
status: init?.status || 302,
|
|
375
|
+
headers
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
function json(data, init) {
|
|
379
|
+
const headers = new Headers(init?.headers);
|
|
380
|
+
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
381
|
+
return new Response(JSON.stringify(data), {
|
|
382
|
+
...init,
|
|
383
|
+
headers
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function defer(data) {
|
|
387
|
+
return Object.assign(data, { __deferred: true });
|
|
388
|
+
}
|
|
389
|
+
function isRedirectResponse(response) {
|
|
390
|
+
return response.status >= 300 && response.status < 400 && response.headers.has("Location");
|
|
391
|
+
}
|
|
392
|
+
function isJsonResponse(response) {
|
|
393
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
394
|
+
return contentType.includes("application/json");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/server.ts
|
|
398
|
+
async function runLoader(loader, context) {
|
|
399
|
+
try {
|
|
400
|
+
const result = await loader(context);
|
|
401
|
+
return result;
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error("Loader error:", error);
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async function runAction(action, context) {
|
|
408
|
+
try {
|
|
409
|
+
const result = await action(context);
|
|
410
|
+
if (result instanceof Response) {
|
|
411
|
+
return result;
|
|
412
|
+
}
|
|
413
|
+
return result;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error("Action error:", error);
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function hydrateLoaderData(pathname, data) {
|
|
420
|
+
const serialized = JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
421
|
+
return `<script>
|
|
422
|
+
window.__FLIGHT_LOADER_DATA__ = window.__FLIGHT_LOADER_DATA__ || {};
|
|
423
|
+
window.__FLIGHT_LOADER_DATA__["${pathname}"] = ${serialized};
|
|
424
|
+
</script>`;
|
|
425
|
+
}
|
|
426
|
+
export {
|
|
427
|
+
Form,
|
|
428
|
+
LRUCache,
|
|
429
|
+
asyncData,
|
|
430
|
+
cancelRequest,
|
|
431
|
+
clearCache,
|
|
432
|
+
clearDataStores,
|
|
433
|
+
configureCache,
|
|
434
|
+
createFetchHydrationScript,
|
|
435
|
+
createHydrationScript,
|
|
436
|
+
defer,
|
|
437
|
+
fetchData,
|
|
438
|
+
getActionData,
|
|
439
|
+
getCache,
|
|
440
|
+
getCachedData,
|
|
441
|
+
getLoaderData,
|
|
442
|
+
hydrateFetchData,
|
|
443
|
+
hydrateFromWindow,
|
|
444
|
+
hydrateLoaderData,
|
|
445
|
+
invalidate,
|
|
446
|
+
invalidateCache,
|
|
447
|
+
isClient,
|
|
448
|
+
isJsonResponse,
|
|
449
|
+
isRedirectResponse,
|
|
450
|
+
isServer,
|
|
451
|
+
json,
|
|
452
|
+
prefetch,
|
|
453
|
+
redirect,
|
|
454
|
+
runAction,
|
|
455
|
+
runLoader,
|
|
456
|
+
setActionData,
|
|
457
|
+
setCachedData,
|
|
458
|
+
setLoaderData,
|
|
459
|
+
use,
|
|
460
|
+
useActionData,
|
|
461
|
+
useFetcher,
|
|
462
|
+
useLoaderData
|
|
463
|
+
};
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { U as UseFetchOptions, k as UseFetchReturn, l as UseAsyncDataOptions, m as UseAsyncDataReturn } from './cache-DU1v4CKe.js';
|
|
2
|
+
export { q as clearCache, o as configureCache, g as getCachedData, i as invalidate, r as invalidateCache, p as prefetch, s as setCachedData } from './cache-DU1v4CKe.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @flightdev/data - React Adapter
|
|
6
|
+
*
|
|
7
|
+
* React hooks for data fetching using Flight's core.
|
|
8
|
+
* Uses React 18+ hooks: useState, useEffect, useCallback, useRef.
|
|
9
|
+
*
|
|
10
|
+
* @module @flightdev/data/react
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* React hook for data fetching with caching and deduplication
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* function Users() {
|
|
19
|
+
* const { data, pending, error, refresh } = useFetch<User[]>('/api/users');
|
|
20
|
+
*
|
|
21
|
+
* if (pending) return <p>Loading...</p>;
|
|
22
|
+
* if (error) return <p>Error: {error.message}</p>;
|
|
23
|
+
*
|
|
24
|
+
* return <ul>{data.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
declare function useFetch<T = unknown>(url: string | null, options?: UseFetchOptions<T>): UseFetchReturn<T>;
|
|
29
|
+
/**
|
|
30
|
+
* React hook for custom async data fetching
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* function Analytics() {
|
|
35
|
+
* const { data, pending, execute } = useAsyncData(
|
|
36
|
+
* 'analytics-weekly',
|
|
37
|
+
* () => analyticsAPI.getWeeklyReport()
|
|
38
|
+
* );
|
|
39
|
+
*
|
|
40
|
+
* return (
|
|
41
|
+
* <div>
|
|
42
|
+
* <button onClick={execute}>Refresh</button>
|
|
43
|
+
* {pending ? <Spinner /> : <Chart data={data} />}
|
|
44
|
+
* </div>
|
|
45
|
+
* );
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare function useAsyncData<T = unknown>(key: string, fetcher: () => Promise<T>, options?: UseAsyncDataOptions<T>): UseAsyncDataReturn<T>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Hydrate data on client startup
|
|
53
|
+
* Call this before React hydration
|
|
54
|
+
*/
|
|
55
|
+
declare function hydrateFlightData(): void;
|
|
56
|
+
|
|
57
|
+
export { hydrateFlightData, useAsyncData, useFetch };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asyncData,
|
|
3
|
+
clearCache,
|
|
4
|
+
configureCache,
|
|
5
|
+
fetchData,
|
|
6
|
+
getCachedData,
|
|
7
|
+
hydrateFetchData,
|
|
8
|
+
invalidate,
|
|
9
|
+
invalidateCache,
|
|
10
|
+
prefetch,
|
|
11
|
+
setCachedData
|
|
12
|
+
} from "./chunk-NOWQL3EM.js";
|
|
13
|
+
|
|
14
|
+
// src/react.ts
|
|
15
|
+
import { useState, useEffect, useCallback, useRef } from "react";
|
|
16
|
+
var isBrowser = typeof window !== "undefined";
|
|
17
|
+
function useFetch(url, options = {}) {
|
|
18
|
+
const { immediate = true } = options;
|
|
19
|
+
const optionsRef = useRef(options);
|
|
20
|
+
optionsRef.current = options;
|
|
21
|
+
const [state, setState] = useState({
|
|
22
|
+
data: options.default,
|
|
23
|
+
error: void 0,
|
|
24
|
+
status: "idle",
|
|
25
|
+
isStale: false
|
|
26
|
+
});
|
|
27
|
+
const [pending, setPending] = useState(immediate && !!url);
|
|
28
|
+
const execute = useCallback(async () => {
|
|
29
|
+
if (!url) return;
|
|
30
|
+
setPending(true);
|
|
31
|
+
const result = await fetchData(url, optionsRef.current);
|
|
32
|
+
setState(result);
|
|
33
|
+
setPending(false);
|
|
34
|
+
}, [url]);
|
|
35
|
+
const refresh = useCallback(async () => {
|
|
36
|
+
if (!url) return;
|
|
37
|
+
const key = optionsRef.current.key ?? url;
|
|
38
|
+
invalidate(key);
|
|
39
|
+
await execute();
|
|
40
|
+
}, [url, execute]);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (immediate && url) {
|
|
43
|
+
execute();
|
|
44
|
+
}
|
|
45
|
+
}, [immediate, url, execute]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!optionsRef.current.revalidateOnFocus || !isBrowser) return;
|
|
48
|
+
const onFocus = () => execute();
|
|
49
|
+
window.addEventListener("focus", onFocus);
|
|
50
|
+
return () => window.removeEventListener("focus", onFocus);
|
|
51
|
+
}, [execute]);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!optionsRef.current.revalidateOnReconnect || !isBrowser) return;
|
|
54
|
+
const onOnline = () => execute();
|
|
55
|
+
window.addEventListener("online", onOnline);
|
|
56
|
+
return () => window.removeEventListener("online", onOnline);
|
|
57
|
+
}, [execute]);
|
|
58
|
+
return {
|
|
59
|
+
data: state.data,
|
|
60
|
+
pending,
|
|
61
|
+
error: state.error,
|
|
62
|
+
status: state.status,
|
|
63
|
+
isStale: state.isStale,
|
|
64
|
+
refresh,
|
|
65
|
+
execute
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function useAsyncData(key, fetcher, options = {}) {
|
|
69
|
+
const { immediate = true } = options;
|
|
70
|
+
const optionsRef = useRef(options);
|
|
71
|
+
const fetcherRef = useRef(fetcher);
|
|
72
|
+
optionsRef.current = options;
|
|
73
|
+
fetcherRef.current = fetcher;
|
|
74
|
+
const [state, setState] = useState({
|
|
75
|
+
data: options.default,
|
|
76
|
+
error: void 0,
|
|
77
|
+
status: "idle",
|
|
78
|
+
isStale: false
|
|
79
|
+
});
|
|
80
|
+
const [pending, setPending] = useState(immediate);
|
|
81
|
+
const execute = useCallback(async () => {
|
|
82
|
+
setPending(true);
|
|
83
|
+
const result = await asyncData(key, fetcherRef.current, optionsRef.current);
|
|
84
|
+
setState(result);
|
|
85
|
+
setPending(false);
|
|
86
|
+
}, [key]);
|
|
87
|
+
const refresh = useCallback(async () => {
|
|
88
|
+
invalidate(key);
|
|
89
|
+
await execute();
|
|
90
|
+
}, [key, execute]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (immediate) {
|
|
93
|
+
execute();
|
|
94
|
+
}
|
|
95
|
+
}, [immediate, execute]);
|
|
96
|
+
return {
|
|
97
|
+
data: state.data,
|
|
98
|
+
pending,
|
|
99
|
+
error: state.error,
|
|
100
|
+
status: state.status,
|
|
101
|
+
execute,
|
|
102
|
+
refresh
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function hydrateFlightData() {
|
|
106
|
+
hydrateFetchData();
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
clearCache,
|
|
110
|
+
configureCache,
|
|
111
|
+
getCachedData,
|
|
112
|
+
hydrateFlightData,
|
|
113
|
+
invalidate,
|
|
114
|
+
invalidateCache,
|
|
115
|
+
prefetch,
|
|
116
|
+
setCachedData,
|
|
117
|
+
useAsyncData,
|
|
118
|
+
useFetch
|
|
119
|
+
};
|