enlace 0.0.1-beta.10 → 0.0.1-beta.11
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 +133 -25
- package/dist/hook/index.d.mts +20 -15
- package/dist/hook/index.d.ts +20 -15
- package/dist/hook/index.js +60 -22
- package/dist/hook/index.mjs +63 -23
- package/dist/index.d.mts +17 -12
- package/dist/index.d.ts +17 -12
- package/dist/index.js +10 -6
- package/dist/index.mjs +10 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,37 +85,108 @@ api.users[123].get(); // GET /users/123
|
|
|
85
85
|
api.users[123].profile.get(); // GET /users/123/profile
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
### Endpoint
|
|
88
|
+
### Endpoint Types
|
|
89
89
|
|
|
90
|
-
The `Endpoint` type
|
|
90
|
+
The `Endpoint` type helpers let you define response data, request body, query params, formData, and error types.
|
|
91
|
+
|
|
92
|
+
#### `Endpoint<TData, TBody?, TError?>`
|
|
93
|
+
|
|
94
|
+
For endpoints with JSON body:
|
|
91
95
|
|
|
92
96
|
```typescript
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
import { Endpoint } from "enlace";
|
|
98
|
+
|
|
99
|
+
type ApiSchema = {
|
|
100
|
+
posts: {
|
|
101
|
+
$get: Post[]; // Direct type (simplest)
|
|
102
|
+
$post: Endpoint<Post, CreatePost>; // Data + Body
|
|
103
|
+
$put: Endpoint<Post, UpdatePost, ValidationError>; // Data + Body + Error
|
|
104
|
+
$delete: void; // void response
|
|
105
|
+
$patch: Endpoint<Post, never, NotFoundError>; // Custom error without body
|
|
106
|
+
};
|
|
107
|
+
};
|
|
95
108
|
```
|
|
96
109
|
|
|
97
|
-
|
|
110
|
+
#### `EndpointWithQuery<TData, TQuery, TError?>`
|
|
111
|
+
|
|
112
|
+
For endpoints with typed query parameters:
|
|
98
113
|
|
|
99
114
|
```typescript
|
|
115
|
+
import { EndpointWithQuery } from "enlace";
|
|
116
|
+
|
|
100
117
|
type ApiSchema = {
|
|
118
|
+
users: {
|
|
119
|
+
$get: EndpointWithQuery<User[], { page: number; limit: number; search?: string }>;
|
|
120
|
+
};
|
|
101
121
|
posts: {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
$get: EndpointWithQuery<Post[], { status: "draft" | "published" }, ApiError>;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Usage - query params are fully typed
|
|
127
|
+
const { data } = useAPI((api) => api.users.get({ query: { page: 1, limit: 10 } }));
|
|
128
|
+
// api.users.get({ query: { foo: "bar" } }); // ✗ Error: 'foo' does not exist
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### `EndpointWithFormData<TData, TFormData, TError?>`
|
|
132
|
+
|
|
133
|
+
For file uploads (multipart/form-data):
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { EndpointWithFormData } from "enlace";
|
|
137
|
+
|
|
138
|
+
type ApiSchema = {
|
|
139
|
+
uploads: {
|
|
140
|
+
$post: EndpointWithFormData<Upload, { file: Blob | File; name: string }>;
|
|
141
|
+
};
|
|
142
|
+
avatars: {
|
|
143
|
+
$post: EndpointWithFormData<Avatar, { image: File }, UploadError>;
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Usage - formData is automatically converted to FormData
|
|
148
|
+
const { trigger } = useAPI((api) => api.uploads.post);
|
|
149
|
+
trigger({
|
|
150
|
+
formData: {
|
|
151
|
+
file: selectedFile, // File object
|
|
152
|
+
name: "document.pdf", // String - converted automatically
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
// → Sends as multipart/form-data
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**FormData conversion rules:**
|
|
105
159
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
160
|
+
| Type | Conversion |
|
|
161
|
+
|------|------------|
|
|
162
|
+
| `File` / `Blob` | Appended directly |
|
|
163
|
+
| `string` / `number` / `boolean` | Converted to string |
|
|
164
|
+
| `object` (nested) | JSON stringified |
|
|
165
|
+
| `array` of primitives | Each item appended separately |
|
|
166
|
+
| `array` of files | Each file appended with same key |
|
|
109
167
|
|
|
110
|
-
|
|
111
|
-
// Overrides global error type for this endpoint
|
|
112
|
-
$put: Endpoint<Post, UpdatePost, ValidationError>;
|
|
168
|
+
#### `EndpointFull<T>`
|
|
113
169
|
|
|
114
|
-
|
|
115
|
-
$delete: void;
|
|
170
|
+
Object-style for complex endpoints:
|
|
116
171
|
|
|
117
|
-
|
|
118
|
-
|
|
172
|
+
```typescript
|
|
173
|
+
import { EndpointFull } from "enlace";
|
|
174
|
+
|
|
175
|
+
type ApiSchema = {
|
|
176
|
+
products: {
|
|
177
|
+
$post: EndpointFull<{
|
|
178
|
+
data: Product;
|
|
179
|
+
body: CreateProduct;
|
|
180
|
+
query: { categoryId: string };
|
|
181
|
+
error: ValidationError;
|
|
182
|
+
}>;
|
|
183
|
+
};
|
|
184
|
+
files: {
|
|
185
|
+
$post: EndpointFull<{
|
|
186
|
+
data: FileUpload;
|
|
187
|
+
formData: { file: File; description: string };
|
|
188
|
+
query: { folder: string };
|
|
189
|
+
}>;
|
|
119
190
|
};
|
|
120
191
|
};
|
|
121
192
|
```
|
|
@@ -541,8 +612,9 @@ type UseEnlaceSelectorResult<TMethod> = {
|
|
|
541
612
|
|
|
542
613
|
```typescript
|
|
543
614
|
type RequestOptions = {
|
|
544
|
-
query?:
|
|
545
|
-
body?: TBody; // Request body
|
|
615
|
+
query?: TQuery; // Query parameters (typed when using EndpointWithQuery/EndpointFull)
|
|
616
|
+
body?: TBody; // Request body (JSON)
|
|
617
|
+
formData?: TFormData; // FormData fields (auto-converted, for file uploads)
|
|
546
618
|
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>); // Request headers
|
|
547
619
|
tags?: string[]; // Cache tags (GET only)
|
|
548
620
|
revalidateTags?: string[]; // Tags to invalidate after mutation
|
|
@@ -623,7 +695,7 @@ const useAPI = createEnlaceHookNext<ApiSchema, ApiError>(
|
|
|
623
695
|
"/api",
|
|
624
696
|
{},
|
|
625
697
|
{
|
|
626
|
-
|
|
698
|
+
serverRevalidator: revalidateAction,
|
|
627
699
|
}
|
|
628
700
|
);
|
|
629
701
|
```
|
|
@@ -637,13 +709,46 @@ function CreatePost() {
|
|
|
637
709
|
const handleCreate = () => {
|
|
638
710
|
trigger({
|
|
639
711
|
body: { title: "New Post" },
|
|
640
|
-
revalidateTags: ["posts"], // Passed to
|
|
641
|
-
revalidatePaths: ["/posts"], // Passed to
|
|
712
|
+
revalidateTags: ["posts"], // Passed to serverRevalidator
|
|
713
|
+
revalidatePaths: ["/posts"], // Passed to serverRevalidator
|
|
642
714
|
});
|
|
643
715
|
};
|
|
644
716
|
}
|
|
645
717
|
```
|
|
646
718
|
|
|
719
|
+
### CSR-Heavy Projects
|
|
720
|
+
|
|
721
|
+
For projects that primarily use client-side rendering with minimal SSR, you can disable server-side revalidation by default:
|
|
722
|
+
|
|
723
|
+
```typescript
|
|
724
|
+
const useAPI = createEnlaceHookNext<ApiSchema, ApiError>(
|
|
725
|
+
"/api",
|
|
726
|
+
{},
|
|
727
|
+
{
|
|
728
|
+
serverRevalidator: revalidateAction,
|
|
729
|
+
skipServerRevalidation: true, // Disable server revalidation by default
|
|
730
|
+
}
|
|
731
|
+
);
|
|
732
|
+
|
|
733
|
+
// Mutations won't trigger server revalidation by default
|
|
734
|
+
await trigger({ body: { title: "New Post" } });
|
|
735
|
+
|
|
736
|
+
// Opt-in to server revalidation when needed
|
|
737
|
+
await trigger({ body: { title: "New Post" }, serverRevalidate: true });
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Per-Request Server Revalidation Control
|
|
741
|
+
|
|
742
|
+
Override the global setting for individual requests:
|
|
743
|
+
|
|
744
|
+
```typescript
|
|
745
|
+
// Skip server revalidation for this request
|
|
746
|
+
await trigger({ body: data, serverRevalidate: false });
|
|
747
|
+
|
|
748
|
+
// Force server revalidation for this request
|
|
749
|
+
await trigger({ body: data, serverRevalidate: true });
|
|
750
|
+
```
|
|
751
|
+
|
|
647
752
|
### Next.js Request Options
|
|
648
753
|
|
|
649
754
|
```typescript
|
|
@@ -652,7 +757,7 @@ api.posts.get({
|
|
|
652
757
|
revalidate: 60, // ISR revalidation (seconds)
|
|
653
758
|
revalidateTags: ["posts"], // Tags to invalidate after mutation
|
|
654
759
|
revalidatePaths: ["/"], // Paths to revalidate after mutation
|
|
655
|
-
|
|
760
|
+
serverRevalidate: true, // Control server-side revalidation per-request
|
|
656
761
|
});
|
|
657
762
|
```
|
|
658
763
|
|
|
@@ -710,7 +815,10 @@ type EnlaceHookOptions = {
|
|
|
710
815
|
|
|
711
816
|
### Re-exports from enlace-core
|
|
712
817
|
|
|
713
|
-
- `Endpoint` — Type helper for
|
|
818
|
+
- `Endpoint` — Type helper for endpoints with JSON body
|
|
819
|
+
- `EndpointWithQuery` — Type helper for endpoints with typed query params
|
|
820
|
+
- `EndpointWithFormData` — Type helper for file upload endpoints
|
|
821
|
+
- `EndpointFull` — Object-style type helper for complex endpoints
|
|
714
822
|
- `EnlaceResponse` — Response type
|
|
715
823
|
- `EnlaceOptions` — Fetch options type
|
|
716
824
|
|
package/dist/hook/index.d.mts
CHANGED
|
@@ -108,7 +108,7 @@ declare function createEnlaceHookReact<TSchema = unknown, TDefaultError = unknow
|
|
|
108
108
|
* @param tags - Cache tags to revalidate
|
|
109
109
|
* @param paths - URL paths to revalidate
|
|
110
110
|
*/
|
|
111
|
-
type
|
|
111
|
+
type ServerRevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
112
112
|
/** Next.js-specific options (third argument for createEnlaceNext) */
|
|
113
113
|
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
114
114
|
/**
|
|
@@ -117,32 +117,37 @@ type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateT
|
|
|
117
117
|
* @example
|
|
118
118
|
* ```ts
|
|
119
119
|
* createEnlaceNext("http://localhost:3000/api/", {}, {
|
|
120
|
-
*
|
|
120
|
+
* serverRevalidator: (tags, paths) => revalidateServerAction(tags, paths)
|
|
121
121
|
* });
|
|
122
122
|
* ```
|
|
123
123
|
*/
|
|
124
|
-
|
|
124
|
+
serverRevalidator?: ServerRevalidateHandler;
|
|
125
|
+
/**
|
|
126
|
+
* Skip server-side revalidation by default for all mutations.
|
|
127
|
+
* Individual requests can override with serverRevalidate: true.
|
|
128
|
+
* Useful for CSR-heavy apps where server cache invalidation is rarely needed.
|
|
129
|
+
* @default false
|
|
130
|
+
*/
|
|
131
|
+
skipServerRevalidation?: boolean;
|
|
125
132
|
};
|
|
126
133
|
/** Next.js hook options (third argument for createEnlaceHookNext) - extends React's EnlaceHookOptions */
|
|
127
|
-
type NextHookOptions = EnlaceHookOptions & Pick<NextOptions, "
|
|
134
|
+
type NextHookOptions = EnlaceHookOptions & Pick<NextOptions, "serverRevalidator" | "skipServerRevalidation">;
|
|
128
135
|
/** Per-request options for Next.js fetch - extends React's base options */
|
|
129
136
|
type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
130
137
|
/** Time in seconds to revalidate, or false to disable */
|
|
131
138
|
revalidate?: number | false;
|
|
132
139
|
/**
|
|
133
|
-
* URL paths to revalidate after mutation
|
|
134
|
-
*
|
|
135
|
-
* You must implement the revalidation logic in the revalidator.
|
|
140
|
+
* URL paths to revalidate after mutation.
|
|
141
|
+
* Passed to the serverRevalidator handler.
|
|
136
142
|
*/
|
|
137
143
|
revalidatePaths?: string[];
|
|
138
144
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* Eg. you don't fetch any data on server component and you might want to skip the overhead of revalidation.
|
|
145
|
+
* Control server-side revalidation for this specific request.
|
|
146
|
+
* - true: Force server revalidation
|
|
147
|
+
* - false: Skip server revalidation
|
|
148
|
+
* When undefined, follows the global skipServerRevalidation setting.
|
|
144
149
|
*/
|
|
145
|
-
|
|
150
|
+
serverRevalidate?: boolean;
|
|
146
151
|
};
|
|
147
152
|
type NextQueryFn<TSchema, TData, TError, TDefaultError = unknown> = QueryFn<TSchema, TData, TError, TDefaultError, NextRequestOptionsBase>;
|
|
148
153
|
type NextSelectorFn<TSchema, TMethod, TDefaultError = unknown> = SelectorFn<TSchema, TMethod, TDefaultError, NextRequestOptionsBase>;
|
|
@@ -154,11 +159,11 @@ type NextEnlaceHook<TSchema, TDefaultError = unknown> = {
|
|
|
154
159
|
|
|
155
160
|
/**
|
|
156
161
|
* Creates a React hook for making API calls in Next.js Client Components.
|
|
157
|
-
* Uses Next.js-specific features like
|
|
162
|
+
* Uses Next.js-specific features like serverRevalidator for server-side cache invalidation.
|
|
158
163
|
*
|
|
159
164
|
* @example
|
|
160
165
|
* const useAPI = createEnlaceHookNext<ApiSchema>('https://api.com', {}, {
|
|
161
|
-
*
|
|
166
|
+
* serverRevalidator: (tags) => revalidateTagsAction(tags),
|
|
162
167
|
* staleTime: 5000,
|
|
163
168
|
* });
|
|
164
169
|
*
|
package/dist/hook/index.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ declare function createEnlaceHookReact<TSchema = unknown, TDefaultError = unknow
|
|
|
108
108
|
* @param tags - Cache tags to revalidate
|
|
109
109
|
* @param paths - URL paths to revalidate
|
|
110
110
|
*/
|
|
111
|
-
type
|
|
111
|
+
type ServerRevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
112
112
|
/** Next.js-specific options (third argument for createEnlaceNext) */
|
|
113
113
|
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
114
114
|
/**
|
|
@@ -117,32 +117,37 @@ type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateT
|
|
|
117
117
|
* @example
|
|
118
118
|
* ```ts
|
|
119
119
|
* createEnlaceNext("http://localhost:3000/api/", {}, {
|
|
120
|
-
*
|
|
120
|
+
* serverRevalidator: (tags, paths) => revalidateServerAction(tags, paths)
|
|
121
121
|
* });
|
|
122
122
|
* ```
|
|
123
123
|
*/
|
|
124
|
-
|
|
124
|
+
serverRevalidator?: ServerRevalidateHandler;
|
|
125
|
+
/**
|
|
126
|
+
* Skip server-side revalidation by default for all mutations.
|
|
127
|
+
* Individual requests can override with serverRevalidate: true.
|
|
128
|
+
* Useful for CSR-heavy apps where server cache invalidation is rarely needed.
|
|
129
|
+
* @default false
|
|
130
|
+
*/
|
|
131
|
+
skipServerRevalidation?: boolean;
|
|
125
132
|
};
|
|
126
133
|
/** Next.js hook options (third argument for createEnlaceHookNext) - extends React's EnlaceHookOptions */
|
|
127
|
-
type NextHookOptions = EnlaceHookOptions & Pick<NextOptions, "
|
|
134
|
+
type NextHookOptions = EnlaceHookOptions & Pick<NextOptions, "serverRevalidator" | "skipServerRevalidation">;
|
|
128
135
|
/** Per-request options for Next.js fetch - extends React's base options */
|
|
129
136
|
type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
130
137
|
/** Time in seconds to revalidate, or false to disable */
|
|
131
138
|
revalidate?: number | false;
|
|
132
139
|
/**
|
|
133
|
-
* URL paths to revalidate after mutation
|
|
134
|
-
*
|
|
135
|
-
* You must implement the revalidation logic in the revalidator.
|
|
140
|
+
* URL paths to revalidate after mutation.
|
|
141
|
+
* Passed to the serverRevalidator handler.
|
|
136
142
|
*/
|
|
137
143
|
revalidatePaths?: string[];
|
|
138
144
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* Eg. you don't fetch any data on server component and you might want to skip the overhead of revalidation.
|
|
145
|
+
* Control server-side revalidation for this specific request.
|
|
146
|
+
* - true: Force server revalidation
|
|
147
|
+
* - false: Skip server revalidation
|
|
148
|
+
* When undefined, follows the global skipServerRevalidation setting.
|
|
144
149
|
*/
|
|
145
|
-
|
|
150
|
+
serverRevalidate?: boolean;
|
|
146
151
|
};
|
|
147
152
|
type NextQueryFn<TSchema, TData, TError, TDefaultError = unknown> = QueryFn<TSchema, TData, TError, TDefaultError, NextRequestOptionsBase>;
|
|
148
153
|
type NextSelectorFn<TSchema, TMethod, TDefaultError = unknown> = SelectorFn<TSchema, TMethod, TDefaultError, NextRequestOptionsBase>;
|
|
@@ -154,11 +159,11 @@ type NextEnlaceHook<TSchema, TDefaultError = unknown> = {
|
|
|
154
159
|
|
|
155
160
|
/**
|
|
156
161
|
* Creates a React hook for making API calls in Next.js Client Components.
|
|
157
|
-
* Uses Next.js-specific features like
|
|
162
|
+
* Uses Next.js-specific features like serverRevalidator for server-side cache invalidation.
|
|
158
163
|
*
|
|
159
164
|
* @example
|
|
160
165
|
* const useAPI = createEnlaceHookNext<ApiSchema>('https://api.com', {}, {
|
|
161
|
-
*
|
|
166
|
+
* serverRevalidator: (tags) => revalidateTagsAction(tags),
|
|
162
167
|
* staleTime: 5000,
|
|
163
168
|
* });
|
|
164
169
|
*
|
package/dist/hook/index.js
CHANGED
|
@@ -49,7 +49,8 @@ function hookReducer(state, action) {
|
|
|
49
49
|
return {
|
|
50
50
|
...state,
|
|
51
51
|
loading: state.data === void 0,
|
|
52
|
-
fetching: true
|
|
52
|
+
fetching: true,
|
|
53
|
+
error: void 0
|
|
53
54
|
};
|
|
54
55
|
case "FETCH_SUCCESS":
|
|
55
56
|
return {
|
|
@@ -78,12 +79,21 @@ function generateTags(path) {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
// src/utils/sortObjectKeys.ts
|
|
81
|
-
function sortObjectKeys(obj) {
|
|
82
|
+
function sortObjectKeys(obj, seen = /* @__PURE__ */ new WeakSet()) {
|
|
82
83
|
if (obj === null || typeof obj !== "object") return obj;
|
|
83
|
-
if (
|
|
84
|
+
if (seen.has(obj)) {
|
|
85
|
+
return "[Circular]";
|
|
86
|
+
}
|
|
87
|
+
seen.add(obj);
|
|
88
|
+
if (Array.isArray(obj)) {
|
|
89
|
+
return obj.map((item) => sortObjectKeys(item, seen));
|
|
90
|
+
}
|
|
84
91
|
return Object.keys(obj).sort().reduce(
|
|
85
92
|
(sorted, key) => {
|
|
86
|
-
sorted[key] = sortObjectKeys(
|
|
93
|
+
sorted[key] = sortObjectKeys(
|
|
94
|
+
obj[key],
|
|
95
|
+
seen
|
|
96
|
+
);
|
|
87
97
|
return sorted;
|
|
88
98
|
},
|
|
89
99
|
{}
|
|
@@ -149,10 +159,9 @@ function clearCacheByTags(tags) {
|
|
|
149
159
|
cache.forEach((entry) => {
|
|
150
160
|
const hasMatch = entry.tags.some((tag) => tags.includes(tag));
|
|
151
161
|
if (hasMatch) {
|
|
152
|
-
entry.data = void 0;
|
|
153
|
-
entry.error = void 0;
|
|
154
162
|
entry.timestamp = 0;
|
|
155
163
|
delete entry.promise;
|
|
164
|
+
entry.subscribers.forEach((cb) => cb());
|
|
156
165
|
}
|
|
157
166
|
});
|
|
158
167
|
}
|
|
@@ -196,7 +205,8 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
196
205
|
const cached = getCache(queryKey);
|
|
197
206
|
const hasCachedData = cached?.data !== void 0;
|
|
198
207
|
const isFetching = !!cached?.promise;
|
|
199
|
-
const
|
|
208
|
+
const stale = isStale(queryKey, staleTime);
|
|
209
|
+
const needsFetch = includeNeedsFetch && (!hasCachedData || stale);
|
|
200
210
|
return {
|
|
201
211
|
loading: !hasCachedData && (isFetching || needsFetch),
|
|
202
212
|
fetching: isFetching || needsFetch,
|
|
@@ -240,6 +250,15 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
240
250
|
tags: queryTags
|
|
241
251
|
});
|
|
242
252
|
}
|
|
253
|
+
}).catch((err) => {
|
|
254
|
+
if (mountedRef.current) {
|
|
255
|
+
setCache(queryKey, {
|
|
256
|
+
data: void 0,
|
|
257
|
+
error: err,
|
|
258
|
+
timestamp: Date.now(),
|
|
259
|
+
tags: queryTags
|
|
260
|
+
});
|
|
261
|
+
}
|
|
243
262
|
});
|
|
244
263
|
setCache(queryKey, {
|
|
245
264
|
promise: fetchPromise,
|
|
@@ -384,7 +403,10 @@ function createEnlaceHookReact(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
384
403
|
onSuccess,
|
|
385
404
|
onError
|
|
386
405
|
} = hookOptions;
|
|
387
|
-
const api = (0, import_enlace_core.createEnlace)(baseUrl, defaultOptions, {
|
|
406
|
+
const api = (0, import_enlace_core.createEnlace)(baseUrl, defaultOptions, {
|
|
407
|
+
onSuccess,
|
|
408
|
+
onError
|
|
409
|
+
});
|
|
388
410
|
function useEnlaceHook(selectorOrQuery, queryOptions) {
|
|
389
411
|
let trackingResult = {
|
|
390
412
|
trackedCall: null,
|
|
@@ -394,9 +416,7 @@ function createEnlaceHookReact(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
394
416
|
const trackingProxy = createTrackingProxy((result2) => {
|
|
395
417
|
trackingResult = result2;
|
|
396
418
|
});
|
|
397
|
-
const result = selectorOrQuery(
|
|
398
|
-
trackingProxy
|
|
399
|
-
);
|
|
419
|
+
const result = selectorOrQuery(trackingProxy);
|
|
400
420
|
if (typeof result === "function") {
|
|
401
421
|
const actualResult = selectorOrQuery(api);
|
|
402
422
|
return useSelectorMode({
|
|
@@ -407,6 +427,11 @@ function createEnlaceHookReact(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
407
427
|
autoRevalidateTags
|
|
408
428
|
});
|
|
409
429
|
}
|
|
430
|
+
if (!trackingResult.trackedCall) {
|
|
431
|
+
throw new Error(
|
|
432
|
+
"useAPI query mode requires calling an HTTP method (get, post, etc.). Did you mean to use selector mode? Example: useAPI((api) => api.posts.get())"
|
|
433
|
+
);
|
|
434
|
+
}
|
|
410
435
|
return useQueryMode(
|
|
411
436
|
api,
|
|
412
437
|
trackingResult.trackedCall,
|
|
@@ -425,18 +450,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
425
450
|
const {
|
|
426
451
|
autoGenerateTags = true,
|
|
427
452
|
autoRevalidateTags = true,
|
|
428
|
-
|
|
453
|
+
skipServerRevalidation = false,
|
|
454
|
+
serverRevalidator,
|
|
429
455
|
onSuccess,
|
|
430
456
|
...coreOptions
|
|
431
457
|
} = combinedOptions;
|
|
432
458
|
const isGet = method === "GET";
|
|
433
459
|
const autoTags = generateTags(path);
|
|
434
460
|
const nextOnSuccess = (payload) => {
|
|
435
|
-
if (!isGet
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
461
|
+
if (!isGet) {
|
|
462
|
+
const shouldRevalidateServer = requestOptions?.serverRevalidate ?? !skipServerRevalidation;
|
|
463
|
+
if (shouldRevalidateServer) {
|
|
464
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
465
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
466
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
467
|
+
serverRevalidator?.(revalidateTags, revalidatePaths);
|
|
468
|
+
}
|
|
440
469
|
}
|
|
441
470
|
}
|
|
442
471
|
onSuccess?.(payload);
|
|
@@ -481,11 +510,15 @@ function createEnlaceHookNext(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
481
510
|
staleTime = 0,
|
|
482
511
|
...nextOptions
|
|
483
512
|
} = hookOptions;
|
|
484
|
-
const api = createEnlaceNext(
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
513
|
+
const api = createEnlaceNext(
|
|
514
|
+
baseUrl,
|
|
515
|
+
defaultOptions,
|
|
516
|
+
{
|
|
517
|
+
autoGenerateTags,
|
|
518
|
+
autoRevalidateTags,
|
|
519
|
+
...nextOptions
|
|
520
|
+
}
|
|
521
|
+
);
|
|
489
522
|
function useEnlaceHook(selectorOrQuery, queryOptions) {
|
|
490
523
|
let trackedCall = null;
|
|
491
524
|
let selectorPath = null;
|
|
@@ -506,6 +539,11 @@ function createEnlaceHookNext(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
506
539
|
autoRevalidateTags
|
|
507
540
|
});
|
|
508
541
|
}
|
|
542
|
+
if (!trackedCall) {
|
|
543
|
+
throw new Error(
|
|
544
|
+
"useAPI query mode requires calling an HTTP method (get, post, etc.). Did you mean to use selector mode? Example: useAPI((api) => api.posts.get())"
|
|
545
|
+
);
|
|
546
|
+
}
|
|
509
547
|
return useQueryMode(
|
|
510
548
|
api,
|
|
511
549
|
trackedCall,
|
package/dist/hook/index.mjs
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
// src/react/createEnlaceHookReact.ts
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createEnlace
|
|
7
|
+
} from "enlace-core";
|
|
6
8
|
|
|
7
9
|
// src/react/useQueryMode.ts
|
|
8
10
|
import { useRef, useReducer, useEffect } from "react";
|
|
@@ -22,7 +24,8 @@ function hookReducer(state, action) {
|
|
|
22
24
|
return {
|
|
23
25
|
...state,
|
|
24
26
|
loading: state.data === void 0,
|
|
25
|
-
fetching: true
|
|
27
|
+
fetching: true,
|
|
28
|
+
error: void 0
|
|
26
29
|
};
|
|
27
30
|
case "FETCH_SUCCESS":
|
|
28
31
|
return {
|
|
@@ -51,12 +54,21 @@ function generateTags(path) {
|
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
// src/utils/sortObjectKeys.ts
|
|
54
|
-
function sortObjectKeys(obj) {
|
|
57
|
+
function sortObjectKeys(obj, seen = /* @__PURE__ */ new WeakSet()) {
|
|
55
58
|
if (obj === null || typeof obj !== "object") return obj;
|
|
56
|
-
if (
|
|
59
|
+
if (seen.has(obj)) {
|
|
60
|
+
return "[Circular]";
|
|
61
|
+
}
|
|
62
|
+
seen.add(obj);
|
|
63
|
+
if (Array.isArray(obj)) {
|
|
64
|
+
return obj.map((item) => sortObjectKeys(item, seen));
|
|
65
|
+
}
|
|
57
66
|
return Object.keys(obj).sort().reduce(
|
|
58
67
|
(sorted, key) => {
|
|
59
|
-
sorted[key] = sortObjectKeys(
|
|
68
|
+
sorted[key] = sortObjectKeys(
|
|
69
|
+
obj[key],
|
|
70
|
+
seen
|
|
71
|
+
);
|
|
60
72
|
return sorted;
|
|
61
73
|
},
|
|
62
74
|
{}
|
|
@@ -122,10 +134,9 @@ function clearCacheByTags(tags) {
|
|
|
122
134
|
cache.forEach((entry) => {
|
|
123
135
|
const hasMatch = entry.tags.some((tag) => tags.includes(tag));
|
|
124
136
|
if (hasMatch) {
|
|
125
|
-
entry.data = void 0;
|
|
126
|
-
entry.error = void 0;
|
|
127
137
|
entry.timestamp = 0;
|
|
128
138
|
delete entry.promise;
|
|
139
|
+
entry.subscribers.forEach((cb) => cb());
|
|
129
140
|
}
|
|
130
141
|
});
|
|
131
142
|
}
|
|
@@ -169,7 +180,8 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
169
180
|
const cached = getCache(queryKey);
|
|
170
181
|
const hasCachedData = cached?.data !== void 0;
|
|
171
182
|
const isFetching = !!cached?.promise;
|
|
172
|
-
const
|
|
183
|
+
const stale = isStale(queryKey, staleTime);
|
|
184
|
+
const needsFetch = includeNeedsFetch && (!hasCachedData || stale);
|
|
173
185
|
return {
|
|
174
186
|
loading: !hasCachedData && (isFetching || needsFetch),
|
|
175
187
|
fetching: isFetching || needsFetch,
|
|
@@ -213,6 +225,15 @@ function useQueryMode(api, trackedCall, options) {
|
|
|
213
225
|
tags: queryTags
|
|
214
226
|
});
|
|
215
227
|
}
|
|
228
|
+
}).catch((err) => {
|
|
229
|
+
if (mountedRef.current) {
|
|
230
|
+
setCache(queryKey, {
|
|
231
|
+
data: void 0,
|
|
232
|
+
error: err,
|
|
233
|
+
timestamp: Date.now(),
|
|
234
|
+
tags: queryTags
|
|
235
|
+
});
|
|
236
|
+
}
|
|
216
237
|
});
|
|
217
238
|
setCache(queryKey, {
|
|
218
239
|
promise: fetchPromise,
|
|
@@ -357,7 +378,10 @@ function createEnlaceHookReact(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
357
378
|
onSuccess,
|
|
358
379
|
onError
|
|
359
380
|
} = hookOptions;
|
|
360
|
-
const api = createEnlace(baseUrl, defaultOptions, {
|
|
381
|
+
const api = createEnlace(baseUrl, defaultOptions, {
|
|
382
|
+
onSuccess,
|
|
383
|
+
onError
|
|
384
|
+
});
|
|
361
385
|
function useEnlaceHook(selectorOrQuery, queryOptions) {
|
|
362
386
|
let trackingResult = {
|
|
363
387
|
trackedCall: null,
|
|
@@ -367,9 +391,7 @@ function createEnlaceHookReact(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
367
391
|
const trackingProxy = createTrackingProxy((result2) => {
|
|
368
392
|
trackingResult = result2;
|
|
369
393
|
});
|
|
370
|
-
const result = selectorOrQuery(
|
|
371
|
-
trackingProxy
|
|
372
|
-
);
|
|
394
|
+
const result = selectorOrQuery(trackingProxy);
|
|
373
395
|
if (typeof result === "function") {
|
|
374
396
|
const actualResult = selectorOrQuery(api);
|
|
375
397
|
return useSelectorMode({
|
|
@@ -380,6 +402,11 @@ function createEnlaceHookReact(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
380
402
|
autoRevalidateTags
|
|
381
403
|
});
|
|
382
404
|
}
|
|
405
|
+
if (!trackingResult.trackedCall) {
|
|
406
|
+
throw new Error(
|
|
407
|
+
"useAPI query mode requires calling an HTTP method (get, post, etc.). Did you mean to use selector mode? Example: useAPI((api) => api.posts.get())"
|
|
408
|
+
);
|
|
409
|
+
}
|
|
383
410
|
return useQueryMode(
|
|
384
411
|
api,
|
|
385
412
|
trackingResult.trackedCall,
|
|
@@ -402,18 +429,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
402
429
|
const {
|
|
403
430
|
autoGenerateTags = true,
|
|
404
431
|
autoRevalidateTags = true,
|
|
405
|
-
|
|
432
|
+
skipServerRevalidation = false,
|
|
433
|
+
serverRevalidator,
|
|
406
434
|
onSuccess,
|
|
407
435
|
...coreOptions
|
|
408
436
|
} = combinedOptions;
|
|
409
437
|
const isGet = method === "GET";
|
|
410
438
|
const autoTags = generateTags(path);
|
|
411
439
|
const nextOnSuccess = (payload) => {
|
|
412
|
-
if (!isGet
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
440
|
+
if (!isGet) {
|
|
441
|
+
const shouldRevalidateServer = requestOptions?.serverRevalidate ?? !skipServerRevalidation;
|
|
442
|
+
if (shouldRevalidateServer) {
|
|
443
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
444
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
445
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
446
|
+
serverRevalidator?.(revalidateTags, revalidatePaths);
|
|
447
|
+
}
|
|
417
448
|
}
|
|
418
449
|
}
|
|
419
450
|
onSuccess?.(payload);
|
|
@@ -458,11 +489,15 @@ function createEnlaceHookNext(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
458
489
|
staleTime = 0,
|
|
459
490
|
...nextOptions
|
|
460
491
|
} = hookOptions;
|
|
461
|
-
const api = createEnlaceNext(
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
492
|
+
const api = createEnlaceNext(
|
|
493
|
+
baseUrl,
|
|
494
|
+
defaultOptions,
|
|
495
|
+
{
|
|
496
|
+
autoGenerateTags,
|
|
497
|
+
autoRevalidateTags,
|
|
498
|
+
...nextOptions
|
|
499
|
+
}
|
|
500
|
+
);
|
|
466
501
|
function useEnlaceHook(selectorOrQuery, queryOptions) {
|
|
467
502
|
let trackedCall = null;
|
|
468
503
|
let selectorPath = null;
|
|
@@ -483,6 +518,11 @@ function createEnlaceHookNext(baseUrl, defaultOptions = {}, hookOptions = {}) {
|
|
|
483
518
|
autoRevalidateTags
|
|
484
519
|
});
|
|
485
520
|
}
|
|
521
|
+
if (!trackedCall) {
|
|
522
|
+
throw new Error(
|
|
523
|
+
"useAPI query mode requires calling an HTTP method (get, post, etc.). Did you mean to use selector mode? Example: useAPI((api) => api.posts.get())"
|
|
524
|
+
);
|
|
525
|
+
}
|
|
486
526
|
return useQueryMode(
|
|
487
527
|
api,
|
|
488
528
|
trackedCall,
|
package/dist/index.d.mts
CHANGED
|
@@ -43,7 +43,7 @@ type EnlaceHookOptions = {
|
|
|
43
43
|
* @param tags - Cache tags to revalidate
|
|
44
44
|
* @param paths - URL paths to revalidate
|
|
45
45
|
*/
|
|
46
|
-
type
|
|
46
|
+
type ServerRevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
47
47
|
/** Next.js-specific options (third argument for createEnlaceNext) */
|
|
48
48
|
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
49
49
|
/**
|
|
@@ -52,30 +52,35 @@ type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateT
|
|
|
52
52
|
* @example
|
|
53
53
|
* ```ts
|
|
54
54
|
* createEnlaceNext("http://localhost:3000/api/", {}, {
|
|
55
|
-
*
|
|
55
|
+
* serverRevalidator: (tags, paths) => revalidateServerAction(tags, paths)
|
|
56
56
|
* });
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
serverRevalidator?: ServerRevalidateHandler;
|
|
60
|
+
/**
|
|
61
|
+
* Skip server-side revalidation by default for all mutations.
|
|
62
|
+
* Individual requests can override with serverRevalidate: true.
|
|
63
|
+
* Useful for CSR-heavy apps where server cache invalidation is rarely needed.
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
skipServerRevalidation?: boolean;
|
|
60
67
|
};
|
|
61
68
|
/** Per-request options for Next.js fetch - extends React's base options */
|
|
62
69
|
type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
63
70
|
/** Time in seconds to revalidate, or false to disable */
|
|
64
71
|
revalidate?: number | false;
|
|
65
72
|
/**
|
|
66
|
-
* URL paths to revalidate after mutation
|
|
67
|
-
*
|
|
68
|
-
* You must implement the revalidation logic in the revalidator.
|
|
73
|
+
* URL paths to revalidate after mutation.
|
|
74
|
+
* Passed to the serverRevalidator handler.
|
|
69
75
|
*/
|
|
70
76
|
revalidatePaths?: string[];
|
|
71
77
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* Eg. you don't fetch any data on server component and you might want to skip the overhead of revalidation.
|
|
78
|
+
* Control server-side revalidation for this specific request.
|
|
79
|
+
* - true: Force server revalidation
|
|
80
|
+
* - false: Skip server revalidation
|
|
81
|
+
* When undefined, follows the global skipServerRevalidation setting.
|
|
77
82
|
*/
|
|
78
|
-
|
|
83
|
+
serverRevalidate?: boolean;
|
|
79
84
|
};
|
|
80
85
|
|
|
81
86
|
declare function createEnlaceNext<TSchema = unknown, TDefaultError = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions | null, nextOptions?: NextOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, TDefaultError, NextRequestOptionsBase>;
|
package/dist/index.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ type EnlaceHookOptions = {
|
|
|
43
43
|
* @param tags - Cache tags to revalidate
|
|
44
44
|
* @param paths - URL paths to revalidate
|
|
45
45
|
*/
|
|
46
|
-
type
|
|
46
|
+
type ServerRevalidateHandler = (tags: string[], paths: string[]) => void | Promise<void>;
|
|
47
47
|
/** Next.js-specific options (third argument for createEnlaceNext) */
|
|
48
48
|
type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateTags"> & EnlaceCallbacks & {
|
|
49
49
|
/**
|
|
@@ -52,30 +52,35 @@ type NextOptions = Pick<EnlaceHookOptions, "autoGenerateTags" | "autoRevalidateT
|
|
|
52
52
|
* @example
|
|
53
53
|
* ```ts
|
|
54
54
|
* createEnlaceNext("http://localhost:3000/api/", {}, {
|
|
55
|
-
*
|
|
55
|
+
* serverRevalidator: (tags, paths) => revalidateServerAction(tags, paths)
|
|
56
56
|
* });
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
serverRevalidator?: ServerRevalidateHandler;
|
|
60
|
+
/**
|
|
61
|
+
* Skip server-side revalidation by default for all mutations.
|
|
62
|
+
* Individual requests can override with serverRevalidate: true.
|
|
63
|
+
* Useful for CSR-heavy apps where server cache invalidation is rarely needed.
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
skipServerRevalidation?: boolean;
|
|
60
67
|
};
|
|
61
68
|
/** Per-request options for Next.js fetch - extends React's base options */
|
|
62
69
|
type NextRequestOptionsBase = ReactRequestOptionsBase & {
|
|
63
70
|
/** Time in seconds to revalidate, or false to disable */
|
|
64
71
|
revalidate?: number | false;
|
|
65
72
|
/**
|
|
66
|
-
* URL paths to revalidate after mutation
|
|
67
|
-
*
|
|
68
|
-
* You must implement the revalidation logic in the revalidator.
|
|
73
|
+
* URL paths to revalidate after mutation.
|
|
74
|
+
* Passed to the serverRevalidator handler.
|
|
69
75
|
*/
|
|
70
76
|
revalidatePaths?: string[];
|
|
71
77
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* Eg. you don't fetch any data on server component and you might want to skip the overhead of revalidation.
|
|
78
|
+
* Control server-side revalidation for this specific request.
|
|
79
|
+
* - true: Force server revalidation
|
|
80
|
+
* - false: Skip server revalidation
|
|
81
|
+
* When undefined, follows the global skipServerRevalidation setting.
|
|
77
82
|
*/
|
|
78
|
-
|
|
83
|
+
serverRevalidate?: boolean;
|
|
79
84
|
};
|
|
80
85
|
|
|
81
86
|
declare function createEnlaceNext<TSchema = unknown, TDefaultError = unknown>(baseUrl: string, defaultOptions?: EnlaceOptions | null, nextOptions?: NextOptions): unknown extends TSchema ? WildcardClient<NextRequestOptionsBase> : EnlaceClient<TSchema, TDefaultError, NextRequestOptionsBase>;
|
package/dist/index.js
CHANGED
|
@@ -42,18 +42,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
42
42
|
const {
|
|
43
43
|
autoGenerateTags = true,
|
|
44
44
|
autoRevalidateTags = true,
|
|
45
|
-
|
|
45
|
+
skipServerRevalidation = false,
|
|
46
|
+
serverRevalidator,
|
|
46
47
|
onSuccess,
|
|
47
48
|
...coreOptions
|
|
48
49
|
} = combinedOptions;
|
|
49
50
|
const isGet = method === "GET";
|
|
50
51
|
const autoTags = generateTags(path);
|
|
51
52
|
const nextOnSuccess = (payload) => {
|
|
52
|
-
if (!isGet
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
if (!isGet) {
|
|
54
|
+
const shouldRevalidateServer = requestOptions?.serverRevalidate ?? !skipServerRevalidation;
|
|
55
|
+
if (shouldRevalidateServer) {
|
|
56
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
57
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
58
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
59
|
+
serverRevalidator?.(revalidateTags, revalidatePaths);
|
|
60
|
+
}
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
onSuccess?.(payload);
|
package/dist/index.mjs
CHANGED
|
@@ -21,18 +21,22 @@ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestO
|
|
|
21
21
|
const {
|
|
22
22
|
autoGenerateTags = true,
|
|
23
23
|
autoRevalidateTags = true,
|
|
24
|
-
|
|
24
|
+
skipServerRevalidation = false,
|
|
25
|
+
serverRevalidator,
|
|
25
26
|
onSuccess,
|
|
26
27
|
...coreOptions
|
|
27
28
|
} = combinedOptions;
|
|
28
29
|
const isGet = method === "GET";
|
|
29
30
|
const autoTags = generateTags(path);
|
|
30
31
|
const nextOnSuccess = (payload) => {
|
|
31
|
-
if (!isGet
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
if (!isGet) {
|
|
33
|
+
const shouldRevalidateServer = requestOptions?.serverRevalidate ?? !skipServerRevalidation;
|
|
34
|
+
if (shouldRevalidateServer) {
|
|
35
|
+
const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
|
|
36
|
+
const revalidatePaths = requestOptions?.revalidatePaths ?? [];
|
|
37
|
+
if (revalidateTags.length || revalidatePaths.length) {
|
|
38
|
+
serverRevalidator?.(revalidateTags, revalidatePaths);
|
|
39
|
+
}
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
onSuccess?.(payload);
|