enlace 0.0.1-beta.4 → 0.0.1-beta.5
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/README.md +89 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +21 -15
- package/dist/index.mjs +22 -18
- package/dist/next/hook/index.d.mts +9 -4
- package/dist/next/hook/index.d.ts +9 -4
- package/dist/next/hook/index.js +43 -61
- package/dist/next/hook/index.mjs +44 -64
- package/dist/next/index.d.mts +57 -6
- package/dist/next/index.d.ts +57 -6
- package/dist/next/index.js +26 -48
- package/dist/next/index.mjs +27 -51
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -361,6 +361,92 @@ const useAPI = createEnlaceHook<ApiSchema>(
|
|
|
361
361
|
);
|
|
362
362
|
```
|
|
363
363
|
|
|
364
|
+
### Async Headers
|
|
365
|
+
|
|
366
|
+
Headers can be provided as a static value, sync function, or async function. This is useful when you need to fetch headers dynamically (e.g., auth tokens from async storage):
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// Static headers
|
|
370
|
+
const useAPI = createEnlaceHook<ApiSchema>("https://api.example.com", {
|
|
371
|
+
headers: { Authorization: "Bearer token" },
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Sync function
|
|
375
|
+
const useAPI = createEnlaceHook<ApiSchema>("https://api.example.com", {
|
|
376
|
+
headers: () => ({ Authorization: `Bearer ${getToken()}` }),
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Async function
|
|
380
|
+
const useAPI = createEnlaceHook<ApiSchema>("https://api.example.com", {
|
|
381
|
+
headers: async () => {
|
|
382
|
+
const token = await getTokenFromStorage();
|
|
383
|
+
return { Authorization: `Bearer ${token}` };
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
This also works for per-request headers:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
const { data } = useAPI((api) =>
|
|
392
|
+
api.posts.get({
|
|
393
|
+
headers: async () => {
|
|
394
|
+
const token = await refreshToken();
|
|
395
|
+
return { Authorization: `Bearer ${token}` };
|
|
396
|
+
},
|
|
397
|
+
})
|
|
398
|
+
);
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Global Callbacks
|
|
402
|
+
|
|
403
|
+
You can set up global `onSuccess` and `onError` callbacks that are called for every request:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
const useAPI = createEnlaceHook<ApiSchema>(
|
|
407
|
+
"https://api.example.com",
|
|
408
|
+
{
|
|
409
|
+
headers: { Authorization: "Bearer token" },
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
onSuccess: (payload) => {
|
|
413
|
+
console.log("Request succeeded:", payload.status, payload.data);
|
|
414
|
+
},
|
|
415
|
+
onError: (payload) => {
|
|
416
|
+
if (payload.status === 0) {
|
|
417
|
+
// Network error
|
|
418
|
+
console.error("Network error:", payload.error.message);
|
|
419
|
+
} else {
|
|
420
|
+
// HTTP error (4xx, 5xx)
|
|
421
|
+
console.error("HTTP error:", payload.status, payload.error);
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
**Callback Payloads:**
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
// onSuccess payload
|
|
432
|
+
type EnlaceCallbackPayload<T> = {
|
|
433
|
+
status: number;
|
|
434
|
+
data: T;
|
|
435
|
+
headers: Headers;
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// onError payload (HTTP error or network error)
|
|
439
|
+
type EnlaceErrorCallbackPayload<T> =
|
|
440
|
+
| { status: number; error: T; headers: Headers } // HTTP error
|
|
441
|
+
| { status: 0; error: Error; headers: null }; // Network error
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Use cases:**
|
|
445
|
+
- Global error logging/reporting
|
|
446
|
+
- Toast notifications for all API errors
|
|
447
|
+
- Authentication refresh on 401 errors
|
|
448
|
+
- Analytics tracking
|
|
449
|
+
|
|
364
450
|
## Return Types
|
|
365
451
|
|
|
366
452
|
### Query Mode
|
|
@@ -403,6 +489,7 @@ type UseEnlaceSelectorResult<TMethod> = {
|
|
|
403
489
|
type RequestOptions = {
|
|
404
490
|
query?: Record<string, unknown>; // Query parameters
|
|
405
491
|
body?: TBody; // Request body
|
|
492
|
+
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>); // Request headers
|
|
406
493
|
tags?: string[]; // Cache tags (GET only)
|
|
407
494
|
revalidateTags?: string[]; // Tags to invalidate after mutation
|
|
408
495
|
pathParams?: Record<string, string | number>; // Dynamic path parameters
|
|
@@ -531,6 +618,8 @@ type EnlaceHookOptions = {
|
|
|
531
618
|
autoGenerateTags?: boolean; // default: true
|
|
532
619
|
autoRevalidateTags?: boolean; // default: true
|
|
533
620
|
staleTime?: number; // default: 0
|
|
621
|
+
onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
|
|
622
|
+
onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
|
|
534
623
|
};
|
|
535
624
|
```
|
|
536
625
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EnlaceCallbackPayload, EnlaceErrorCallbackPayload, EnlaceResponse, WildcardClient, EnlaceClient, EnlaceOptions } from 'enlace-core';
|
|
2
2
|
export * from 'enlace-core';
|
|
3
3
|
|
|
4
4
|
/** Per-request options for React hooks */
|
|
@@ -72,7 +72,7 @@ type UseEnlaceSelectorResult<TMethod> = {
|
|
|
72
72
|
loading: boolean;
|
|
73
73
|
fetching: boolean;
|
|
74
74
|
} & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
|
|
75
|
-
|
|
75
|
+
/** Options for createEnlaceHook factory */
|
|
76
76
|
type EnlaceHookOptions = {
|
|
77
77
|
/**
|
|
78
78
|
* Auto-generate cache tags from URL path for GET requests.
|
|
@@ -84,11 +84,17 @@ type EnlaceHookOptions = {
|
|
|
84
84
|
autoRevalidateTags?: boolean;
|
|
85
85
|
/** Time in ms before cached data is considered stale. @default 0 (always stale) */
|
|
86
86
|
staleTime?: number;
|
|
87
|
+
/** Callback called on successful API responses */
|
|
88
|
+
onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
|
|
89
|
+
/** Callback called on error responses (HTTP errors or network failures) */
|
|
90
|
+
onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
|
|
87
91
|
};
|
|
92
|
+
/** Hook type returned by createEnlaceHook */
|
|
88
93
|
type EnlaceHook<TSchema> = {
|
|
89
94
|
<TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: SelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
|
|
90
95
|
<TData, TError>(queryFn: QueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
|
|
91
96
|
};
|
|
97
|
+
|
|
92
98
|
/**
|
|
93
99
|
* Creates a React hook for making API calls.
|
|
94
100
|
* Called at module level to create a reusable hook.
|
|
@@ -111,4 +117,4 @@ declare function onRevalidate(callback: Listener): () => void;
|
|
|
111
117
|
|
|
112
118
|
declare function clearCache(key?: string): void;
|
|
113
119
|
|
|
114
|
-
export { type ApiClient, type EnlaceHookOptions, HTTP_METHODS, type HookState, type QueryFn, type ReactRequestOptionsBase, type SelectorFn, type TrackedCall, type UseEnlaceQueryOptions, type UseEnlaceQueryResult, type UseEnlaceSelectorResult, clearCache, createEnlaceHook, invalidateTags, onRevalidate };
|
|
120
|
+
export { type ApiClient, type EnlaceHook, type EnlaceHookOptions, HTTP_METHODS, type HookState, type QueryFn, type ReactRequestOptionsBase, type SelectorFn, type TrackedCall, type UseEnlaceQueryOptions, type UseEnlaceQueryResult, type UseEnlaceSelectorResult, clearCache, createEnlaceHook, invalidateTags, onRevalidate };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EnlaceCallbackPayload, EnlaceErrorCallbackPayload, EnlaceResponse, WildcardClient, EnlaceClient, EnlaceOptions } from 'enlace-core';
|
|
2
2
|
export * from 'enlace-core';
|
|
3
3
|
|
|
4
4
|
/** Per-request options for React hooks */
|
|
@@ -72,7 +72,7 @@ type UseEnlaceSelectorResult<TMethod> = {
|
|
|
72
72
|
loading: boolean;
|
|
73
73
|
fetching: boolean;
|
|
74
74
|
} & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
|
|
75
|
-
|
|
75
|
+
/** Options for createEnlaceHook factory */
|
|
76
76
|
type EnlaceHookOptions = {
|
|
77
77
|
/**
|
|
78
78
|
* Auto-generate cache tags from URL path for GET requests.
|
|
@@ -84,11 +84,17 @@ type EnlaceHookOptions = {
|
|
|
84
84
|
autoRevalidateTags?: boolean;
|
|
85
85
|
/** Time in ms before cached data is considered stale. @default 0 (always stale) */
|
|
86
86
|
staleTime?: number;
|
|
87
|
+
/** Callback called on successful API responses */
|
|
88
|
+
onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
|
|
89
|
+
/** Callback called on error responses (HTTP errors or network failures) */
|
|
90
|
+
onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
|
|
87
91
|
};
|
|
92
|
+
/** Hook type returned by createEnlaceHook */
|
|
88
93
|
type EnlaceHook<TSchema> = {
|
|
89
94
|
<TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: SelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
|
|
90
95
|
<TData, TError>(queryFn: QueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
|
|
91
96
|
};
|
|
97
|
+
|
|
92
98
|
/**
|
|
93
99
|
* Creates a React hook for making API calls.
|
|
94
100
|
* Called at module level to create a reusable hook.
|
|
@@ -111,4 +117,4 @@ declare function onRevalidate(callback: Listener): () => void;
|
|
|
111
117
|
|
|
112
118
|
declare function clearCache(key?: string): void;
|
|
113
119
|
|
|
114
|
-
export { type ApiClient, type EnlaceHookOptions, HTTP_METHODS, type HookState, type QueryFn, type ReactRequestOptionsBase, type SelectorFn, type TrackedCall, type UseEnlaceQueryOptions, type UseEnlaceQueryResult, type UseEnlaceSelectorResult, clearCache, createEnlaceHook, invalidateTags, onRevalidate };
|
|
120
|
+
export { type ApiClient, type EnlaceHook, type EnlaceHookOptions, HTTP_METHODS, type HookState, type QueryFn, type ReactRequestOptionsBase, type SelectorFn, type TrackedCall, type UseEnlaceQueryOptions, type UseEnlaceQueryResult, type UseEnlaceSelectorResult, clearCache, createEnlaceHook, invalidateTags, onRevalidate };
|
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ var initialState = {
|
|
|
47
47
|
function hookReducer(state, action) {
|
|
48
48
|
switch (action.type) {
|
|
49
49
|
case "RESET":
|
|
50
|
-
return action.state;
|
|
50
|
+
return action.state ?? initialState;
|
|
51
51
|
case "FETCH_START":
|
|
52
52
|
return {
|
|
53
53
|
...state,
|
|
@@ -202,7 +202,10 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
202
202
|
const { autoGenerateTags, staleTime, enabled } = options;
|
|
203
203
|
const queryKey = createQueryKey(trackedCall);
|
|
204
204
|
const requestOptions = trackedCall.options;
|
|
205
|
-
const resolvedPath = resolvePath(
|
|
205
|
+
const resolvedPath = resolvePath(
|
|
206
|
+
trackedCall.path,
|
|
207
|
+
requestOptions?.pathParams
|
|
208
|
+
);
|
|
206
209
|
const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(resolvedPath) : []);
|
|
207
210
|
const getCacheState = (includeNeedsFetch = false) => {
|
|
208
211
|
const cached = getCache(queryKey);
|
|
@@ -217,26 +220,22 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
217
220
|
error: cached?.error
|
|
218
221
|
};
|
|
219
222
|
};
|
|
220
|
-
const [state, dispatch] = (0, import_react.useReducer)(
|
|
223
|
+
const [state, dispatch] = (0, import_react.useReducer)(
|
|
224
|
+
hookReducer,
|
|
225
|
+
null,
|
|
226
|
+
() => getCacheState(true)
|
|
227
|
+
);
|
|
221
228
|
const mountedRef = (0, import_react.useRef)(true);
|
|
222
229
|
const fetchRef = (0, import_react.useRef)(null);
|
|
223
230
|
(0, import_react.useEffect)(() => {
|
|
224
231
|
mountedRef.current = true;
|
|
225
232
|
if (!enabled) {
|
|
226
|
-
dispatch({
|
|
227
|
-
type: "RESET",
|
|
228
|
-
state: { loading: false, fetching: false, ok: void 0, data: void 0, error: void 0 }
|
|
229
|
-
});
|
|
233
|
+
dispatch({ type: "RESET" });
|
|
230
234
|
return () => {
|
|
231
235
|
mountedRef.current = false;
|
|
232
236
|
};
|
|
233
237
|
}
|
|
234
238
|
dispatch({ type: "RESET", state: getCacheState(true) });
|
|
235
|
-
const unsubscribe = subscribeCache(queryKey, () => {
|
|
236
|
-
if (mountedRef.current) {
|
|
237
|
-
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
239
|
const doFetch = () => {
|
|
241
240
|
const cached2 = getCache(queryKey);
|
|
242
241
|
if (cached2?.promise) {
|
|
@@ -252,7 +251,7 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
252
251
|
if (mountedRef.current) {
|
|
253
252
|
setCache(queryKey, {
|
|
254
253
|
data: res.ok ? res.data : void 0,
|
|
255
|
-
error: res.ok ? void 0 : res.error,
|
|
254
|
+
error: res.ok || res.status === 0 ? void 0 : res.error,
|
|
256
255
|
ok: res.ok,
|
|
257
256
|
timestamp: Date.now(),
|
|
258
257
|
tags: queryTags
|
|
@@ -271,6 +270,11 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
271
270
|
} else {
|
|
272
271
|
doFetch();
|
|
273
272
|
}
|
|
273
|
+
const unsubscribe = subscribeCache(queryKey, () => {
|
|
274
|
+
if (mountedRef.current) {
|
|
275
|
+
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
276
|
+
}
|
|
277
|
+
});
|
|
274
278
|
return () => {
|
|
275
279
|
mountedRef.current = false;
|
|
276
280
|
fetchRef.current = null;
|
|
@@ -390,12 +394,14 @@ function useSelectorMode(config) {
|
|
|
390
394
|
|
|
391
395
|
// src/react/createEnlaceHook.ts
|
|
392
396
|
function createEnlaceHook(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
393
|
-
const api = (0, import_enlace_core.createEnlace)(baseUrl, defaultOptions);
|
|
394
397
|
const {
|
|
395
398
|
autoGenerateTags = true,
|
|
396
399
|
autoRevalidateTags = true,
|
|
397
|
-
staleTime = 0
|
|
400
|
+
staleTime = 0,
|
|
401
|
+
onSuccess,
|
|
402
|
+
onError
|
|
398
403
|
} = hookOptions;
|
|
404
|
+
const api = (0, import_enlace_core.createEnlace)(baseUrl, defaultOptions, { onSuccess, onError });
|
|
399
405
|
function useEnlaceHook(selectorOrQuery, queryOptions) {
|
|
400
406
|
let trackingResult = {
|
|
401
407
|
trackedCall: null,
|
package/dist/index.mjs
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
export * from "enlace-core";
|
|
3
3
|
|
|
4
4
|
// src/react/createEnlaceHook.ts
|
|
5
|
-
import {
|
|
6
|
-
createEnlace
|
|
7
|
-
} from "enlace-core";
|
|
5
|
+
import { createEnlace } from "enlace-core";
|
|
8
6
|
|
|
9
7
|
// src/react/useQueryMode.ts
|
|
10
8
|
import { useRef, useReducer, useEffect } from "react";
|
|
@@ -20,7 +18,7 @@ var initialState = {
|
|
|
20
18
|
function hookReducer(state, action) {
|
|
21
19
|
switch (action.type) {
|
|
22
20
|
case "RESET":
|
|
23
|
-
return action.state;
|
|
21
|
+
return action.state ?? initialState;
|
|
24
22
|
case "FETCH_START":
|
|
25
23
|
return {
|
|
26
24
|
...state,
|
|
@@ -175,7 +173,10 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
175
173
|
const { autoGenerateTags, staleTime, enabled } = options;
|
|
176
174
|
const queryKey = createQueryKey(trackedCall);
|
|
177
175
|
const requestOptions = trackedCall.options;
|
|
178
|
-
const resolvedPath = resolvePath(
|
|
176
|
+
const resolvedPath = resolvePath(
|
|
177
|
+
trackedCall.path,
|
|
178
|
+
requestOptions?.pathParams
|
|
179
|
+
);
|
|
179
180
|
const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(resolvedPath) : []);
|
|
180
181
|
const getCacheState = (includeNeedsFetch = false) => {
|
|
181
182
|
const cached = getCache(queryKey);
|
|
@@ -190,26 +191,22 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
190
191
|
error: cached?.error
|
|
191
192
|
};
|
|
192
193
|
};
|
|
193
|
-
const [state, dispatch] = useReducer(
|
|
194
|
+
const [state, dispatch] = useReducer(
|
|
195
|
+
hookReducer,
|
|
196
|
+
null,
|
|
197
|
+
() => getCacheState(true)
|
|
198
|
+
);
|
|
194
199
|
const mountedRef = useRef(true);
|
|
195
200
|
const fetchRef = useRef(null);
|
|
196
201
|
useEffect(() => {
|
|
197
202
|
mountedRef.current = true;
|
|
198
203
|
if (!enabled) {
|
|
199
|
-
dispatch({
|
|
200
|
-
type: "RESET",
|
|
201
|
-
state: { loading: false, fetching: false, ok: void 0, data: void 0, error: void 0 }
|
|
202
|
-
});
|
|
204
|
+
dispatch({ type: "RESET" });
|
|
203
205
|
return () => {
|
|
204
206
|
mountedRef.current = false;
|
|
205
207
|
};
|
|
206
208
|
}
|
|
207
209
|
dispatch({ type: "RESET", state: getCacheState(true) });
|
|
208
|
-
const unsubscribe = subscribeCache(queryKey, () => {
|
|
209
|
-
if (mountedRef.current) {
|
|
210
|
-
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
210
|
const doFetch = () => {
|
|
214
211
|
const cached2 = getCache(queryKey);
|
|
215
212
|
if (cached2?.promise) {
|
|
@@ -225,7 +222,7 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
225
222
|
if (mountedRef.current) {
|
|
226
223
|
setCache(queryKey, {
|
|
227
224
|
data: res.ok ? res.data : void 0,
|
|
228
|
-
error: res.ok ? void 0 : res.error,
|
|
225
|
+
error: res.ok || res.status === 0 ? void 0 : res.error,
|
|
229
226
|
ok: res.ok,
|
|
230
227
|
timestamp: Date.now(),
|
|
231
228
|
tags: queryTags
|
|
@@ -244,6 +241,11 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
244
241
|
} else {
|
|
245
242
|
doFetch();
|
|
246
243
|
}
|
|
244
|
+
const unsubscribe = subscribeCache(queryKey, () => {
|
|
245
|
+
if (mountedRef.current) {
|
|
246
|
+
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
247
|
+
}
|
|
248
|
+
});
|
|
247
249
|
return () => {
|
|
248
250
|
mountedRef.current = false;
|
|
249
251
|
fetchRef.current = null;
|
|
@@ -363,12 +365,14 @@ function useSelectorMode(config) {
|
|
|
363
365
|
|
|
364
366
|
// src/react/createEnlaceHook.ts
|
|
365
367
|
function createEnlaceHook(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
366
|
-
const api = createEnlace(baseUrl, defaultOptions);
|
|
367
368
|
const {
|
|
368
369
|
autoGenerateTags = true,
|
|
369
370
|
autoRevalidateTags = true,
|
|
370
|
-
staleTime = 0
|
|
371
|
+
staleTime = 0,
|
|
372
|
+
onSuccess,
|
|
373
|
+
onError
|
|
371
374
|
} = hookOptions;
|
|
375
|
+
const api = createEnlace(baseUrl, defaultOptions, { onSuccess, onError });
|
|
372
376
|
function useEnlaceHook(selectorOrQuery, queryOptions) {
|
|
373
377
|
let trackingResult = {
|
|
374
378
|
trackedCall: null,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WildcardClient, EnlaceClient, EnlaceResponse, EnlaceOptions } from 'enlace-core';
|
|
1
|
+
import { EnlaceCallbackPayload, EnlaceErrorCallbackPayload, WildcardClient, EnlaceClient, EnlaceResponse, EnlaceCallbacks, EnlaceOptions } from 'enlace-core';
|
|
2
2
|
|
|
3
3
|
/** Per-request options for React hooks */
|
|
4
4
|
type ReactRequestOptionsBase = {
|
|
@@ -58,7 +58,7 @@ type UseEnlaceSelectorResult<TMethod> = {
|
|
|
58
58
|
loading: boolean;
|
|
59
59
|
fetching: boolean;
|
|
60
60
|
} & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
|
|
61
|
-
|
|
61
|
+
/** Options for createEnlaceHook factory */
|
|
62
62
|
type EnlaceHookOptions = {
|
|
63
63
|
/**
|
|
64
64
|
* Auto-generate cache tags from URL path for GET requests.
|
|
@@ -70,6 +70,10 @@ type EnlaceHookOptions = {
|
|
|
70
70
|
autoRevalidateTags?: boolean;
|
|
71
71
|
/** Time in ms before cached data is considered stale. @default 0 (always stale) */
|
|
72
72
|
staleTime?: number;
|
|
73
|
+
/** Callback called on successful API responses */
|
|
74
|
+
onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
|
|
75
|
+
/** Callback called on error responses (HTTP errors or network failures) */
|
|
76
|
+
onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
|
|
73
77
|
};
|
|
74
78
|
|
|
75
79
|
/**
|
|
@@ -79,7 +83,7 @@ type EnlaceHookOptions = {
|
|
|
79
83
|
*/
|
|
80
84
|
type RevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
81
85
|
/** Next.js-specific options (third argument for createEnlace) */
|
|
82
|
-
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & {
|
|
86
|
+
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
83
87
|
/**
|
|
84
88
|
* Handler called after successful mutations to trigger server-side revalidation.
|
|
85
89
|
* Receives auto-generated or manually specified tags and paths.
|
|
@@ -113,13 +117,14 @@ type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
|
113
117
|
*/
|
|
114
118
|
skipRevalidator?: boolean;
|
|
115
119
|
};
|
|
116
|
-
|
|
117
120
|
type NextQueryFn<TSchema, TData, TError> = QueryFn<TSchema, TData, TError, NextRequestOptionsBase>;
|
|
118
121
|
type NextSelectorFn<TSchema, TMethod> = SelectorFn<TSchema, TMethod, NextRequestOptionsBase>;
|
|
122
|
+
/** Hook type returned by Next.js createEnlaceHook */
|
|
119
123
|
type NextEnlaceHook<TSchema> = {
|
|
120
124
|
<TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: NextSelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
|
|
121
125
|
<TData, TError>(queryFn: NextQueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
|
|
122
126
|
};
|
|
127
|
+
|
|
123
128
|
/**
|
|
124
129
|
* Creates a React hook for making API calls in Next.js Client Components.
|
|
125
130
|
* Uses Next.js-specific features like revalidator for server-side cache invalidation.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WildcardClient, EnlaceClient, EnlaceResponse, EnlaceOptions } from 'enlace-core';
|
|
1
|
+
import { EnlaceCallbackPayload, EnlaceErrorCallbackPayload, WildcardClient, EnlaceClient, EnlaceResponse, EnlaceCallbacks, EnlaceOptions } from 'enlace-core';
|
|
2
2
|
|
|
3
3
|
/** Per-request options for React hooks */
|
|
4
4
|
type ReactRequestOptionsBase = {
|
|
@@ -58,7 +58,7 @@ type UseEnlaceSelectorResult<TMethod> = {
|
|
|
58
58
|
loading: boolean;
|
|
59
59
|
fetching: boolean;
|
|
60
60
|
} & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
|
|
61
|
-
|
|
61
|
+
/** Options for createEnlaceHook factory */
|
|
62
62
|
type EnlaceHookOptions = {
|
|
63
63
|
/**
|
|
64
64
|
* Auto-generate cache tags from URL path for GET requests.
|
|
@@ -70,6 +70,10 @@ type EnlaceHookOptions = {
|
|
|
70
70
|
autoRevalidateTags?: boolean;
|
|
71
71
|
/** Time in ms before cached data is considered stale. @default 0 (always stale) */
|
|
72
72
|
staleTime?: number;
|
|
73
|
+
/** Callback called on successful API responses */
|
|
74
|
+
onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
|
|
75
|
+
/** Callback called on error responses (HTTP errors or network failures) */
|
|
76
|
+
onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
|
|
73
77
|
};
|
|
74
78
|
|
|
75
79
|
/**
|
|
@@ -79,7 +83,7 @@ type EnlaceHookOptions = {
|
|
|
79
83
|
*/
|
|
80
84
|
type RevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
81
85
|
/** Next.js-specific options (third argument for createEnlace) */
|
|
82
|
-
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & {
|
|
86
|
+
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
83
87
|
/**
|
|
84
88
|
* Handler called after successful mutations to trigger server-side revalidation.
|
|
85
89
|
* Receives auto-generated or manually specified tags and paths.
|
|
@@ -113,13 +117,14 @@ type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
|
113
117
|
*/
|
|
114
118
|
skipRevalidator?: boolean;
|
|
115
119
|
};
|
|
116
|
-
|
|
117
120
|
type NextQueryFn<TSchema, TData, TError> = QueryFn<TSchema, TData, TError, NextRequestOptionsBase>;
|
|
118
121
|
type NextSelectorFn<TSchema, TMethod> = SelectorFn<TSchema, TMethod, NextRequestOptionsBase>;
|
|
122
|
+
/** Hook type returned by Next.js createEnlaceHook */
|
|
119
123
|
type NextEnlaceHook<TSchema> = {
|
|
120
124
|
<TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: NextSelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
|
|
121
125
|
<TData, TError>(queryFn: NextQueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
|
|
122
126
|
};
|
|
127
|
+
|
|
123
128
|
/**
|
|
124
129
|
* Creates a React hook for making API calls in Next.js Client Components.
|
|
125
130
|
* Uses Next.js-specific features like revalidator for server-side cache invalidation.
|
package/dist/next/hook/index.js
CHANGED
|
@@ -48,20 +48,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
48
48
|
autoGenerateTags = true,
|
|
49
49
|
autoRevalidateTags = true,
|
|
50
50
|
revalidator,
|
|
51
|
-
|
|
52
|
-
...
|
|
51
|
+
onSuccess,
|
|
52
|
+
...coreOptions
|
|
53
53
|
} = combinedOptions;
|
|
54
|
-
const url = (0, import_enlace_core.buildUrl)(baseUrl, path, requestOptions?.query);
|
|
55
|
-
let headers = (0, import_enlace_core.mergeHeaders)(defaultHeaders, requestOptions?.headers);
|
|
56
54
|
const isGet = method === "GET";
|
|
57
55
|
const autoTags = generateTags(path);
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const nextOnSuccess = (payload) => {
|
|
57
|
+
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
58
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
59
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
60
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
61
|
+
revalidator?.(revalidateTags, revalidatePaths);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
onSuccess?.(payload);
|
|
61
65
|
};
|
|
62
|
-
|
|
63
|
-
fetchOptions.cache = requestOptions.cache;
|
|
64
|
-
}
|
|
66
|
+
const nextRequestOptions = { ...requestOptions };
|
|
65
67
|
if (isGet) {
|
|
66
68
|
const tags = requestOptions?.tags ?? (autoGenerateTags ? autoTags : void 0);
|
|
67
69
|
const nextFetchOptions = {};
|
|
@@ -71,51 +73,27 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
71
73
|
if (requestOptions?.revalidate !== void 0) {
|
|
72
74
|
nextFetchOptions.revalidate = requestOptions.revalidate;
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
if (headers) {
|
|
77
|
-
fetchOptions.headers = headers;
|
|
76
|
+
nextRequestOptions.next = nextFetchOptions;
|
|
78
77
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
} else {
|
|
87
|
-
fetchOptions.body = requestOptions.body;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const response = await fetch(url, fetchOptions);
|
|
91
|
-
const contentType = response.headers.get("content-type");
|
|
92
|
-
const isJson = contentType?.includes("application/json");
|
|
93
|
-
if (response.ok) {
|
|
94
|
-
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
95
|
-
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
96
|
-
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
97
|
-
if (revalidateTags.length || revalidatePaths.length) {
|
|
98
|
-
revalidator?.(revalidateTags, revalidatePaths);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
ok: true,
|
|
103
|
-
status: response.status,
|
|
104
|
-
data: isJson ? await response.json() : response
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
return {
|
|
108
|
-
ok: false,
|
|
109
|
-
status: response.status,
|
|
110
|
-
error: isJson ? await response.json() : response
|
|
111
|
-
};
|
|
78
|
+
return (0, import_enlace_core.executeFetch)(
|
|
79
|
+
baseUrl,
|
|
80
|
+
path,
|
|
81
|
+
method,
|
|
82
|
+
{ ...coreOptions, onSuccess: nextOnSuccess },
|
|
83
|
+
nextRequestOptions
|
|
84
|
+
);
|
|
112
85
|
}
|
|
113
86
|
|
|
114
87
|
// src/next/index.ts
|
|
115
88
|
__reExport(next_exports, require("enlace-core"));
|
|
116
89
|
function createEnlace(baseUrl, defaultOptions = {}, nextOptions = {}) {
|
|
117
90
|
const combinedOptions = { ...defaultOptions, ...nextOptions };
|
|
118
|
-
return (0, import_enlace_core2.createProxyHandler)(
|
|
91
|
+
return (0, import_enlace_core2.createProxyHandler)(
|
|
92
|
+
baseUrl,
|
|
93
|
+
combinedOptions,
|
|
94
|
+
[],
|
|
95
|
+
executeNextFetch
|
|
96
|
+
);
|
|
119
97
|
}
|
|
120
98
|
|
|
121
99
|
// src/react/useQueryMode.ts
|
|
@@ -132,7 +110,7 @@ var initialState = {
|
|
|
132
110
|
function hookReducer(state, action) {
|
|
133
111
|
switch (action.type) {
|
|
134
112
|
case "RESET":
|
|
135
|
-
return action.state;
|
|
113
|
+
return action.state ?? initialState;
|
|
136
114
|
case "FETCH_START":
|
|
137
115
|
return {
|
|
138
116
|
...state,
|
|
@@ -275,7 +253,10 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
275
253
|
const { autoGenerateTags, staleTime, enabled } = options;
|
|
276
254
|
const queryKey = createQueryKey(trackedCall);
|
|
277
255
|
const requestOptions = trackedCall.options;
|
|
278
|
-
const resolvedPath = resolvePath(
|
|
256
|
+
const resolvedPath = resolvePath(
|
|
257
|
+
trackedCall.path,
|
|
258
|
+
requestOptions?.pathParams
|
|
259
|
+
);
|
|
279
260
|
const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(resolvedPath) : []);
|
|
280
261
|
const getCacheState = (includeNeedsFetch = false) => {
|
|
281
262
|
const cached = getCache(queryKey);
|
|
@@ -290,26 +271,22 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
290
271
|
error: cached?.error
|
|
291
272
|
};
|
|
292
273
|
};
|
|
293
|
-
const [state, dispatch] = (0, import_react.useReducer)(
|
|
274
|
+
const [state, dispatch] = (0, import_react.useReducer)(
|
|
275
|
+
hookReducer,
|
|
276
|
+
null,
|
|
277
|
+
() => getCacheState(true)
|
|
278
|
+
);
|
|
294
279
|
const mountedRef = (0, import_react.useRef)(true);
|
|
295
280
|
const fetchRef = (0, import_react.useRef)(null);
|
|
296
281
|
(0, import_react.useEffect)(() => {
|
|
297
282
|
mountedRef.current = true;
|
|
298
283
|
if (!enabled) {
|
|
299
|
-
dispatch({
|
|
300
|
-
type: "RESET",
|
|
301
|
-
state: { loading: false, fetching: false, ok: void 0, data: void 0, error: void 0 }
|
|
302
|
-
});
|
|
284
|
+
dispatch({ type: "RESET" });
|
|
303
285
|
return () => {
|
|
304
286
|
mountedRef.current = false;
|
|
305
287
|
};
|
|
306
288
|
}
|
|
307
289
|
dispatch({ type: "RESET", state: getCacheState(true) });
|
|
308
|
-
const unsubscribe = subscribeCache(queryKey, () => {
|
|
309
|
-
if (mountedRef.current) {
|
|
310
|
-
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
290
|
const doFetch = () => {
|
|
314
291
|
const cached2 = getCache(queryKey);
|
|
315
292
|
if (cached2?.promise) {
|
|
@@ -325,7 +302,7 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
325
302
|
if (mountedRef.current) {
|
|
326
303
|
setCache(queryKey, {
|
|
327
304
|
data: res.ok ? res.data : void 0,
|
|
328
|
-
error: res.ok ? void 0 : res.error,
|
|
305
|
+
error: res.ok || res.status === 0 ? void 0 : res.error,
|
|
329
306
|
ok: res.ok,
|
|
330
307
|
timestamp: Date.now(),
|
|
331
308
|
tags: queryTags
|
|
@@ -344,6 +321,11 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
344
321
|
} else {
|
|
345
322
|
doFetch();
|
|
346
323
|
}
|
|
324
|
+
const unsubscribe = subscribeCache(queryKey, () => {
|
|
325
|
+
if (mountedRef.current) {
|
|
326
|
+
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
327
|
+
}
|
|
328
|
+
});
|
|
347
329
|
return () => {
|
|
348
330
|
mountedRef.current = false;
|
|
349
331
|
fetchRef.current = null;
|
package/dist/next/hook/index.mjs
CHANGED
|
@@ -29,9 +29,7 @@ import {
|
|
|
29
29
|
|
|
30
30
|
// src/next/fetch.ts
|
|
31
31
|
import {
|
|
32
|
-
|
|
33
|
-
isJsonBody,
|
|
34
|
-
mergeHeaders
|
|
32
|
+
executeFetch
|
|
35
33
|
} from "enlace-core";
|
|
36
34
|
|
|
37
35
|
// src/utils/generateTags.ts
|
|
@@ -45,20 +43,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
45
43
|
autoGenerateTags = true,
|
|
46
44
|
autoRevalidateTags = true,
|
|
47
45
|
revalidator,
|
|
48
|
-
|
|
49
|
-
...
|
|
46
|
+
onSuccess,
|
|
47
|
+
...coreOptions
|
|
50
48
|
} = combinedOptions;
|
|
51
|
-
const url = buildUrl(baseUrl, path, requestOptions?.query);
|
|
52
|
-
let headers = mergeHeaders(defaultHeaders, requestOptions?.headers);
|
|
53
49
|
const isGet = method === "GET";
|
|
54
50
|
const autoTags = generateTags(path);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
const nextOnSuccess = (payload) => {
|
|
52
|
+
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
53
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
54
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
55
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
56
|
+
revalidator?.(revalidateTags, revalidatePaths);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
onSuccess?.(payload);
|
|
58
60
|
};
|
|
59
|
-
|
|
60
|
-
fetchOptions.cache = requestOptions.cache;
|
|
61
|
-
}
|
|
61
|
+
const nextRequestOptions = { ...requestOptions };
|
|
62
62
|
if (isGet) {
|
|
63
63
|
const tags = requestOptions?.tags ?? (autoGenerateTags ? autoTags : void 0);
|
|
64
64
|
const nextFetchOptions = {};
|
|
@@ -68,44 +68,15 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
68
68
|
if (requestOptions?.revalidate !== void 0) {
|
|
69
69
|
nextFetchOptions.revalidate = requestOptions.revalidate;
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
if (headers) {
|
|
74
|
-
fetchOptions.headers = headers;
|
|
71
|
+
nextRequestOptions.next = nextFetchOptions;
|
|
75
72
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
} else {
|
|
84
|
-
fetchOptions.body = requestOptions.body;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const response = await fetch(url, fetchOptions);
|
|
88
|
-
const contentType = response.headers.get("content-type");
|
|
89
|
-
const isJson = contentType?.includes("application/json");
|
|
90
|
-
if (response.ok) {
|
|
91
|
-
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
92
|
-
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
93
|
-
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
94
|
-
if (revalidateTags.length || revalidatePaths.length) {
|
|
95
|
-
revalidator?.(revalidateTags, revalidatePaths);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return {
|
|
99
|
-
ok: true,
|
|
100
|
-
status: response.status,
|
|
101
|
-
data: isJson ? await response.json() : response
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
return {
|
|
105
|
-
ok: false,
|
|
106
|
-
status: response.status,
|
|
107
|
-
error: isJson ? await response.json() : response
|
|
108
|
-
};
|
|
73
|
+
return executeFetch(
|
|
74
|
+
baseUrl,
|
|
75
|
+
path,
|
|
76
|
+
method,
|
|
77
|
+
{ ...coreOptions, onSuccess: nextOnSuccess },
|
|
78
|
+
nextRequestOptions
|
|
79
|
+
);
|
|
109
80
|
}
|
|
110
81
|
|
|
111
82
|
// src/next/index.ts
|
|
@@ -113,7 +84,12 @@ __reExport(next_exports, enlace_core_star);
|
|
|
113
84
|
import * as enlace_core_star from "enlace-core";
|
|
114
85
|
function createEnlace(baseUrl, defaultOptions = {}, nextOptions = {}) {
|
|
115
86
|
const combinedOptions = { ...defaultOptions, ...nextOptions };
|
|
116
|
-
return createProxyHandler(
|
|
87
|
+
return createProxyHandler(
|
|
88
|
+
baseUrl,
|
|
89
|
+
combinedOptions,
|
|
90
|
+
[],
|
|
91
|
+
executeNextFetch
|
|
92
|
+
);
|
|
117
93
|
}
|
|
118
94
|
|
|
119
95
|
// src/react/useQueryMode.ts
|
|
@@ -130,7 +106,7 @@ var initialState = {
|
|
|
130
106
|
function hookReducer(state, action) {
|
|
131
107
|
switch (action.type) {
|
|
132
108
|
case "RESET":
|
|
133
|
-
return action.state;
|
|
109
|
+
return action.state ?? initialState;
|
|
134
110
|
case "FETCH_START":
|
|
135
111
|
return {
|
|
136
112
|
...state,
|
|
@@ -273,7 +249,10 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
273
249
|
const { autoGenerateTags, staleTime, enabled } = options;
|
|
274
250
|
const queryKey = createQueryKey(trackedCall);
|
|
275
251
|
const requestOptions = trackedCall.options;
|
|
276
|
-
const resolvedPath = resolvePath(
|
|
252
|
+
const resolvedPath = resolvePath(
|
|
253
|
+
trackedCall.path,
|
|
254
|
+
requestOptions?.pathParams
|
|
255
|
+
);
|
|
277
256
|
const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(resolvedPath) : []);
|
|
278
257
|
const getCacheState = (includeNeedsFetch = false) => {
|
|
279
258
|
const cached = getCache(queryKey);
|
|
@@ -288,26 +267,22 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
288
267
|
error: cached?.error
|
|
289
268
|
};
|
|
290
269
|
};
|
|
291
|
-
const [state, dispatch] = useReducer(
|
|
270
|
+
const [state, dispatch] = useReducer(
|
|
271
|
+
hookReducer,
|
|
272
|
+
null,
|
|
273
|
+
() => getCacheState(true)
|
|
274
|
+
);
|
|
292
275
|
const mountedRef = useRef(true);
|
|
293
276
|
const fetchRef = useRef(null);
|
|
294
277
|
useEffect(() => {
|
|
295
278
|
mountedRef.current = true;
|
|
296
279
|
if (!enabled) {
|
|
297
|
-
dispatch({
|
|
298
|
-
type: "RESET",
|
|
299
|
-
state: { loading: false, fetching: false, ok: void 0, data: void 0, error: void 0 }
|
|
300
|
-
});
|
|
280
|
+
dispatch({ type: "RESET" });
|
|
301
281
|
return () => {
|
|
302
282
|
mountedRef.current = false;
|
|
303
283
|
};
|
|
304
284
|
}
|
|
305
285
|
dispatch({ type: "RESET", state: getCacheState(true) });
|
|
306
|
-
const unsubscribe = subscribeCache(queryKey, () => {
|
|
307
|
-
if (mountedRef.current) {
|
|
308
|
-
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
286
|
const doFetch = () => {
|
|
312
287
|
const cached2 = getCache(queryKey);
|
|
313
288
|
if (cached2?.promise) {
|
|
@@ -323,7 +298,7 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
323
298
|
if (mountedRef.current) {
|
|
324
299
|
setCache(queryKey, {
|
|
325
300
|
data: res.ok ? res.data : void 0,
|
|
326
|
-
error: res.ok ? void 0 : res.error,
|
|
301
|
+
error: res.ok || res.status === 0 ? void 0 : res.error,
|
|
327
302
|
ok: res.ok,
|
|
328
303
|
timestamp: Date.now(),
|
|
329
304
|
tags: queryTags
|
|
@@ -342,6 +317,11 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
342
317
|
} else {
|
|
343
318
|
doFetch();
|
|
344
319
|
}
|
|
320
|
+
const unsubscribe = subscribeCache(queryKey, () => {
|
|
321
|
+
if (mountedRef.current) {
|
|
322
|
+
dispatch({ type: "SYNC_CACHE", state: getCacheState() });
|
|
323
|
+
}
|
|
324
|
+
});
|
|
345
325
|
return () => {
|
|
346
326
|
mountedRef.current = false;
|
|
347
327
|
fetchRef.current = null;
|
package/dist/next/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EnlaceCallbackPayload, EnlaceErrorCallbackPayload, WildcardClient, EnlaceClient, EnlaceResponse, EnlaceCallbacks, EnlaceOptions } from 'enlace-core';
|
|
2
2
|
export * from 'enlace-core';
|
|
3
|
-
export { EnlaceOptions } from 'enlace-core';
|
|
3
|
+
export { EnlaceCallbacks, EnlaceOptions } from 'enlace-core';
|
|
4
4
|
|
|
5
5
|
/** Per-request options for React hooks */
|
|
6
6
|
type ReactRequestOptionsBase = {
|
|
@@ -21,7 +21,46 @@ type ReactRequestOptionsBase = {
|
|
|
21
21
|
*/
|
|
22
22
|
pathParams?: Record<string, string | number>;
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
/** Options for query mode hooks */
|
|
25
|
+
type UseEnlaceQueryOptions = {
|
|
26
|
+
/**
|
|
27
|
+
* Whether the query should execute.
|
|
28
|
+
* Set to false to skip fetching (useful when ID is "new" or undefined).
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
enabled?: boolean;
|
|
32
|
+
};
|
|
33
|
+
type ApiClient<TSchema, TOptions = ReactRequestOptionsBase> = unknown extends TSchema ? WildcardClient<TOptions> : EnlaceClient<TSchema, TOptions>;
|
|
34
|
+
type QueryFn<TSchema, TData, TError, TOptions = ReactRequestOptionsBase> = (api: ApiClient<TSchema, TOptions>) => Promise<EnlaceResponse<TData, TError>>;
|
|
35
|
+
type SelectorFn<TSchema, TMethod, TOptions = ReactRequestOptionsBase> = (api: ApiClient<TSchema, TOptions>) => TMethod;
|
|
36
|
+
type ExtractData<T> = T extends (...args: any[]) => Promise<EnlaceResponse<infer D, unknown>> ? D : never;
|
|
37
|
+
type ExtractError<T> = T extends (...args: any[]) => Promise<EnlaceResponse<unknown, infer E>> ? E : never;
|
|
38
|
+
/** Discriminated union for hook response state - enables type narrowing on ok check */
|
|
39
|
+
type HookResponseState<TData, TError> = {
|
|
40
|
+
ok: undefined;
|
|
41
|
+
data: undefined;
|
|
42
|
+
error: undefined;
|
|
43
|
+
} | {
|
|
44
|
+
ok: true;
|
|
45
|
+
data: TData;
|
|
46
|
+
error: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
ok: false;
|
|
49
|
+
data: undefined;
|
|
50
|
+
error: TError;
|
|
51
|
+
};
|
|
52
|
+
/** Result when hook is called with query function (auto-fetch mode) */
|
|
53
|
+
type UseEnlaceQueryResult<TData, TError> = {
|
|
54
|
+
loading: boolean;
|
|
55
|
+
fetching: boolean;
|
|
56
|
+
} & HookResponseState<TData, TError>;
|
|
57
|
+
/** Result when hook is called with method selector (trigger mode) */
|
|
58
|
+
type UseEnlaceSelectorResult<TMethod> = {
|
|
59
|
+
trigger: TMethod;
|
|
60
|
+
loading: boolean;
|
|
61
|
+
fetching: boolean;
|
|
62
|
+
} & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
|
|
63
|
+
/** Options for createEnlaceHook factory */
|
|
25
64
|
type EnlaceHookOptions = {
|
|
26
65
|
/**
|
|
27
66
|
* Auto-generate cache tags from URL path for GET requests.
|
|
@@ -33,6 +72,10 @@ type EnlaceHookOptions = {
|
|
|
33
72
|
autoRevalidateTags?: boolean;
|
|
34
73
|
/** Time in ms before cached data is considered stale. @default 0 (always stale) */
|
|
35
74
|
staleTime?: number;
|
|
75
|
+
/** Callback called on successful API responses */
|
|
76
|
+
onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
|
|
77
|
+
/** Callback called on error responses (HTTP errors or network failures) */
|
|
78
|
+
onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
|
|
36
79
|
};
|
|
37
80
|
|
|
38
81
|
/**
|
|
@@ -42,7 +85,7 @@ type EnlaceHookOptions = {
|
|
|
42
85
|
*/
|
|
43
86
|
type RevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
44
87
|
/** Next.js-specific options (third argument for createEnlace) */
|
|
45
|
-
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & {
|
|
88
|
+
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
46
89
|
/**
|
|
47
90
|
* Handler called after successful mutations to trigger server-side revalidation.
|
|
48
91
|
* Receives auto-generated or manually specified tags and paths.
|
|
@@ -76,7 +119,15 @@ type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
|
76
119
|
*/
|
|
77
120
|
skipRevalidator?: boolean;
|
|
78
121
|
};
|
|
122
|
+
type NextApiClient<TSchema> = ApiClient<TSchema, NextRequestOptionsBase>;
|
|
123
|
+
type NextQueryFn<TSchema, TData, TError> = QueryFn<TSchema, TData, TError, NextRequestOptionsBase>;
|
|
124
|
+
type NextSelectorFn<TSchema, TMethod> = SelectorFn<TSchema, TMethod, NextRequestOptionsBase>;
|
|
125
|
+
/** Hook type returned by Next.js createEnlaceHook */
|
|
126
|
+
type NextEnlaceHook<TSchema> = {
|
|
127
|
+
<TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: NextSelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
|
|
128
|
+
<TData, TError>(queryFn: NextQueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
|
|
129
|
+
};
|
|
79
130
|
|
|
80
|
-
declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions, nextOptions?: NextOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, NextRequestOptionsBase>;
|
|
131
|
+
declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions | null, nextOptions?: NextOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, NextRequestOptionsBase>;
|
|
81
132
|
|
|
82
|
-
export { type NextHookOptions, type NextOptions, type NextRequestOptionsBase, type RevalidateHandler, createEnlace };
|
|
133
|
+
export { type NextApiClient, type NextEnlaceHook, type NextHookOptions, type NextOptions, type NextQueryFn, type NextRequestOptionsBase, type NextSelectorFn, type RevalidateHandler, createEnlace };
|
package/dist/next/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EnlaceCallbackPayload, EnlaceErrorCallbackPayload, WildcardClient, EnlaceClient, EnlaceResponse, EnlaceCallbacks, EnlaceOptions } from 'enlace-core';
|
|
2
2
|
export * from 'enlace-core';
|
|
3
|
-
export { EnlaceOptions } from 'enlace-core';
|
|
3
|
+
export { EnlaceCallbacks, EnlaceOptions } from 'enlace-core';
|
|
4
4
|
|
|
5
5
|
/** Per-request options for React hooks */
|
|
6
6
|
type ReactRequestOptionsBase = {
|
|
@@ -21,7 +21,46 @@ type ReactRequestOptionsBase = {
|
|
|
21
21
|
*/
|
|
22
22
|
pathParams?: Record<string, string | number>;
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
/** Options for query mode hooks */
|
|
25
|
+
type UseEnlaceQueryOptions = {
|
|
26
|
+
/**
|
|
27
|
+
* Whether the query should execute.
|
|
28
|
+
* Set to false to skip fetching (useful when ID is "new" or undefined).
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
enabled?: boolean;
|
|
32
|
+
};
|
|
33
|
+
type ApiClient<TSchema, TOptions = ReactRequestOptionsBase> = unknown extends TSchema ? WildcardClient<TOptions> : EnlaceClient<TSchema, TOptions>;
|
|
34
|
+
type QueryFn<TSchema, TData, TError, TOptions = ReactRequestOptionsBase> = (api: ApiClient<TSchema, TOptions>) => Promise<EnlaceResponse<TData, TError>>;
|
|
35
|
+
type SelectorFn<TSchema, TMethod, TOptions = ReactRequestOptionsBase> = (api: ApiClient<TSchema, TOptions>) => TMethod;
|
|
36
|
+
type ExtractData<T> = T extends (...args: any[]) => Promise<EnlaceResponse<infer D, unknown>> ? D : never;
|
|
37
|
+
type ExtractError<T> = T extends (...args: any[]) => Promise<EnlaceResponse<unknown, infer E>> ? E : never;
|
|
38
|
+
/** Discriminated union for hook response state - enables type narrowing on ok check */
|
|
39
|
+
type HookResponseState<TData, TError> = {
|
|
40
|
+
ok: undefined;
|
|
41
|
+
data: undefined;
|
|
42
|
+
error: undefined;
|
|
43
|
+
} | {
|
|
44
|
+
ok: true;
|
|
45
|
+
data: TData;
|
|
46
|
+
error: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
ok: false;
|
|
49
|
+
data: undefined;
|
|
50
|
+
error: TError;
|
|
51
|
+
};
|
|
52
|
+
/** Result when hook is called with query function (auto-fetch mode) */
|
|
53
|
+
type UseEnlaceQueryResult<TData, TError> = {
|
|
54
|
+
loading: boolean;
|
|
55
|
+
fetching: boolean;
|
|
56
|
+
} & HookResponseState<TData, TError>;
|
|
57
|
+
/** Result when hook is called with method selector (trigger mode) */
|
|
58
|
+
type UseEnlaceSelectorResult<TMethod> = {
|
|
59
|
+
trigger: TMethod;
|
|
60
|
+
loading: boolean;
|
|
61
|
+
fetching: boolean;
|
|
62
|
+
} & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
|
|
63
|
+
/** Options for createEnlaceHook factory */
|
|
25
64
|
type EnlaceHookOptions = {
|
|
26
65
|
/**
|
|
27
66
|
* Auto-generate cache tags from URL path for GET requests.
|
|
@@ -33,6 +72,10 @@ type EnlaceHookOptions = {
|
|
|
33
72
|
autoRevalidateTags?: boolean;
|
|
34
73
|
/** Time in ms before cached data is considered stale. @default 0 (always stale) */
|
|
35
74
|
staleTime?: number;
|
|
75
|
+
/** Callback called on successful API responses */
|
|
76
|
+
onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
|
|
77
|
+
/** Callback called on error responses (HTTP errors or network failures) */
|
|
78
|
+
onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
|
|
36
79
|
};
|
|
37
80
|
|
|
38
81
|
/**
|
|
@@ -42,7 +85,7 @@ type EnlaceHookOptions = {
|
|
|
42
85
|
*/
|
|
43
86
|
type RevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
44
87
|
/** Next.js-specific options (third argument for createEnlace) */
|
|
45
|
-
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & {
|
|
88
|
+
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
46
89
|
/**
|
|
47
90
|
* Handler called after successful mutations to trigger server-side revalidation.
|
|
48
91
|
* Receives auto-generated or manually specified tags and paths.
|
|
@@ -76,7 +119,15 @@ type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
|
76
119
|
*/
|
|
77
120
|
skipRevalidator?: boolean;
|
|
78
121
|
};
|
|
122
|
+
type NextApiClient<TSchema> = ApiClient<TSchema, NextRequestOptionsBase>;
|
|
123
|
+
type NextQueryFn<TSchema, TData, TError> = QueryFn<TSchema, TData, TError, NextRequestOptionsBase>;
|
|
124
|
+
type NextSelectorFn<TSchema, TMethod> = SelectorFn<TSchema, TMethod, NextRequestOptionsBase>;
|
|
125
|
+
/** Hook type returned by Next.js createEnlaceHook */
|
|
126
|
+
type NextEnlaceHook<TSchema> = {
|
|
127
|
+
<TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: NextSelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
|
|
128
|
+
<TData, TError>(queryFn: NextQueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
|
|
129
|
+
};
|
|
79
130
|
|
|
80
|
-
declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions, nextOptions?: NextOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, NextRequestOptionsBase>;
|
|
131
|
+
declare function createEnlace<TSchema = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions | null, nextOptions?: NextOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, NextRequestOptionsBase>;
|
|
81
132
|
|
|
82
|
-
export { type NextHookOptions, type NextOptions, type NextRequestOptionsBase, type RevalidateHandler, createEnlace };
|
|
133
|
+
export { type NextApiClient, type NextEnlaceHook, type NextHookOptions, type NextOptions, type NextQueryFn, type NextRequestOptionsBase, type NextSelectorFn, type RevalidateHandler, createEnlace };
|
package/dist/next/index.js
CHANGED
|
@@ -40,20 +40,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
40
40
|
autoGenerateTags = true,
|
|
41
41
|
autoRevalidateTags = true,
|
|
42
42
|
revalidator,
|
|
43
|
-
|
|
44
|
-
...
|
|
43
|
+
onSuccess,
|
|
44
|
+
...coreOptions
|
|
45
45
|
} = combinedOptions;
|
|
46
|
-
const url = (0, import_enlace_core.buildUrl)(baseUrl, path, requestOptions?.query);
|
|
47
|
-
let headers = (0, import_enlace_core.mergeHeaders)(defaultHeaders, requestOptions?.headers);
|
|
48
46
|
const isGet = method === "GET";
|
|
49
47
|
const autoTags = generateTags(path);
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
const nextOnSuccess = (payload) => {
|
|
49
|
+
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
50
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
51
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
52
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
53
|
+
revalidator?.(revalidateTags, revalidatePaths);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
onSuccess?.(payload);
|
|
53
57
|
};
|
|
54
|
-
|
|
55
|
-
fetchOptions.cache = requestOptions.cache;
|
|
56
|
-
}
|
|
58
|
+
const nextRequestOptions = { ...requestOptions };
|
|
57
59
|
if (isGet) {
|
|
58
60
|
const tags = requestOptions?.tags ?? (autoGenerateTags ? autoTags : void 0);
|
|
59
61
|
const nextFetchOptions = {};
|
|
@@ -63,49 +65,25 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
63
65
|
if (requestOptions?.revalidate !== void 0) {
|
|
64
66
|
nextFetchOptions.revalidate = requestOptions.revalidate;
|
|
65
67
|
}
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
if (headers) {
|
|
69
|
-
fetchOptions.headers = headers;
|
|
68
|
+
nextRequestOptions.next = nextFetchOptions;
|
|
70
69
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} else {
|
|
79
|
-
fetchOptions.body = requestOptions.body;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const response = await fetch(url, fetchOptions);
|
|
83
|
-
const contentType = response.headers.get("content-type");
|
|
84
|
-
const isJson = contentType?.includes("application/json");
|
|
85
|
-
if (response.ok) {
|
|
86
|
-
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
87
|
-
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
88
|
-
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
89
|
-
if (revalidateTags.length || revalidatePaths.length) {
|
|
90
|
-
revalidator?.(revalidateTags, revalidatePaths);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return {
|
|
94
|
-
ok: true,
|
|
95
|
-
status: response.status,
|
|
96
|
-
data: isJson ? await response.json() : response
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
ok: false,
|
|
101
|
-
status: response.status,
|
|
102
|
-
error: isJson ? await response.json() : response
|
|
103
|
-
};
|
|
70
|
+
return (0, import_enlace_core.executeFetch)(
|
|
71
|
+
baseUrl,
|
|
72
|
+
path,
|
|
73
|
+
method,
|
|
74
|
+
{ ...coreOptions, onSuccess: nextOnSuccess },
|
|
75
|
+
nextRequestOptions
|
|
76
|
+
);
|
|
104
77
|
}
|
|
105
78
|
|
|
106
79
|
// src/next/index.ts
|
|
107
80
|
__reExport(next_exports, require("enlace-core"), module.exports);
|
|
108
81
|
function createEnlace(baseUrl, defaultOptions = {}, nextOptions = {}) {
|
|
109
82
|
const combinedOptions = { ...defaultOptions, ...nextOptions };
|
|
110
|
-
return (0, import_enlace_core2.createProxyHandler)(
|
|
83
|
+
return (0, import_enlace_core2.createProxyHandler)(
|
|
84
|
+
baseUrl,
|
|
85
|
+
combinedOptions,
|
|
86
|
+
[],
|
|
87
|
+
executeNextFetch
|
|
88
|
+
);
|
|
111
89
|
}
|
package/dist/next/index.mjs
CHANGED
|
@@ -5,9 +5,7 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/next/fetch.ts
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
isJsonBody,
|
|
10
|
-
mergeHeaders
|
|
8
|
+
executeFetch
|
|
11
9
|
} from "enlace-core";
|
|
12
10
|
|
|
13
11
|
// src/utils/generateTags.ts
|
|
@@ -21,20 +19,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
21
19
|
autoGenerateTags = true,
|
|
22
20
|
autoRevalidateTags = true,
|
|
23
21
|
revalidator,
|
|
24
|
-
|
|
25
|
-
...
|
|
22
|
+
onSuccess,
|
|
23
|
+
...coreOptions
|
|
26
24
|
} = combinedOptions;
|
|
27
|
-
const url = buildUrl(baseUrl, path, requestOptions?.query);
|
|
28
|
-
let headers = mergeHeaders(defaultHeaders, requestOptions?.headers);
|
|
29
25
|
const isGet = method === "GET";
|
|
30
26
|
const autoTags = generateTags(path);
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
const nextOnSuccess = (payload) => {
|
|
28
|
+
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
29
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
30
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
31
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
32
|
+
revalidator?.(revalidateTags, revalidatePaths);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
onSuccess?.(payload);
|
|
34
36
|
};
|
|
35
|
-
|
|
36
|
-
fetchOptions.cache = requestOptions.cache;
|
|
37
|
-
}
|
|
37
|
+
const nextRequestOptions = { ...requestOptions };
|
|
38
38
|
if (isGet) {
|
|
39
39
|
const tags = requestOptions?.tags ?? (autoGenerateTags ? autoTags : void 0);
|
|
40
40
|
const nextFetchOptions = {};
|
|
@@ -44,51 +44,27 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
44
44
|
if (requestOptions?.revalidate !== void 0) {
|
|
45
45
|
nextFetchOptions.revalidate = requestOptions.revalidate;
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
if (headers) {
|
|
50
|
-
fetchOptions.headers = headers;
|
|
47
|
+
nextRequestOptions.next = nextFetchOptions;
|
|
51
48
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
} else {
|
|
60
|
-
fetchOptions.body = requestOptions.body;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const response = await fetch(url, fetchOptions);
|
|
64
|
-
const contentType = response.headers.get("content-type");
|
|
65
|
-
const isJson = contentType?.includes("application/json");
|
|
66
|
-
if (response.ok) {
|
|
67
|
-
if (!isGet && !requestOptions?.skipRevalidator) {
|
|
68
|
-
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
69
|
-
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
70
|
-
if (revalidateTags.length || revalidatePaths.length) {
|
|
71
|
-
revalidator?.(revalidateTags, revalidatePaths);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
ok: true,
|
|
76
|
-
status: response.status,
|
|
77
|
-
data: isJson ? await response.json() : response
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
ok: false,
|
|
82
|
-
status: response.status,
|
|
83
|
-
error: isJson ? await response.json() : response
|
|
84
|
-
};
|
|
49
|
+
return executeFetch(
|
|
50
|
+
baseUrl,
|
|
51
|
+
path,
|
|
52
|
+
method,
|
|
53
|
+
{ ...coreOptions, onSuccess: nextOnSuccess },
|
|
54
|
+
nextRequestOptions
|
|
55
|
+
);
|
|
85
56
|
}
|
|
86
57
|
|
|
87
58
|
// src/next/index.ts
|
|
88
59
|
export * from "enlace-core";
|
|
89
60
|
function createEnlace(baseUrl, defaultOptions = {}, nextOptions = {}) {
|
|
90
61
|
const combinedOptions = { ...defaultOptions, ...nextOptions };
|
|
91
|
-
return createProxyHandler(
|
|
62
|
+
return createProxyHandler(
|
|
63
|
+
baseUrl,
|
|
64
|
+
combinedOptions,
|
|
65
|
+
[],
|
|
66
|
+
executeNextFetch
|
|
67
|
+
);
|
|
92
68
|
}
|
|
93
69
|
export {
|
|
94
70
|
createEnlace
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "enlace",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"enlace-core": "0.0.1-beta.
|
|
26
|
+
"enlace-core": "0.0.1-beta.3"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"react": "^19"
|