enlace 0.0.1-beta.3 → 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 CHANGED
@@ -117,6 +117,34 @@ function Posts({ page, limit }: { page: number; limit: number }) {
117
117
  - Returns cached data while revalidating
118
118
  - **Request deduplication** — identical requests from multiple components trigger only one fetch
119
119
 
120
+ ### Conditional Fetching
121
+
122
+ Skip fetching with the `enabled` option:
123
+
124
+ ```typescript
125
+ function ProductForm({ id }: { id: string | "new" }) {
126
+ // Skip fetching when creating a new product
127
+ const { data, loading } = useAPI(
128
+ (api) => api.products[id].get(),
129
+ { enabled: id !== "new" }
130
+ );
131
+
132
+ if (id === "new") return <CreateProductForm />;
133
+ if (loading) return <div>Loading...</div>;
134
+ return <EditProductForm product={data} />;
135
+ }
136
+ ```
137
+
138
+ ```typescript
139
+ // Also useful when waiting for a dependency
140
+ function UserPosts({ userId }: { userId: string | undefined }) {
141
+ const { data } = useAPI(
142
+ (api) => api.users[userId!].posts.get(),
143
+ { enabled: userId !== undefined }
144
+ );
145
+ }
146
+ ```
147
+
120
148
  ```typescript
121
149
  function Post({ id }: { id: number }) {
122
150
  // Automatically re-fetches when `id` or query values change
@@ -186,6 +214,56 @@ function CreatePost() {
186
214
  }
187
215
  ```
188
216
 
217
+ ### Dynamic Path Parameters
218
+
219
+ Use `:paramName` syntax for dynamic IDs passed at trigger time:
220
+
221
+ ```typescript
222
+ function PostList({ posts }: { posts: Post[] }) {
223
+ // Define once with :id placeholder
224
+ const { trigger, loading } = useAPI((api) => api.posts[":id"].delete);
225
+
226
+ const handleDelete = (postId: number) => {
227
+ // Pass the actual ID when triggering
228
+ trigger({ pathParams: { id: postId } });
229
+ };
230
+
231
+ return (
232
+ <ul>
233
+ {posts.map((post) => (
234
+ <li key={post.id}>
235
+ {post.title}
236
+ <button onClick={() => handleDelete(post.id)} disabled={loading}>
237
+ Delete
238
+ </button>
239
+ </li>
240
+ ))}
241
+ </ul>
242
+ );
243
+ }
244
+ ```
245
+
246
+ **Multiple path parameters:**
247
+
248
+ ```typescript
249
+ const { trigger } = useAPI((api) => api.users[":userId"].posts[":postId"].delete);
250
+
251
+ trigger({ pathParams: { userId: "1", postId: "42" } });
252
+ // → DELETE /users/1/posts/42
253
+ ```
254
+
255
+ **With request body:**
256
+
257
+ ```typescript
258
+ const { trigger } = useAPI((api) => api.products[":id"].patch);
259
+
260
+ trigger({
261
+ pathParams: { id: "123" },
262
+ body: { name: "Updated Product" },
263
+ });
264
+ // → PATCH /products/123 with body
265
+ ```
266
+
189
267
  ## Caching & Auto-Revalidation
190
268
 
191
269
  ### Automatic Cache Tags (Zero Config)
@@ -283,11 +361,106 @@ const useAPI = createEnlaceHook<ApiSchema>(
283
361
  );
284
362
  ```
285
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
+
286
450
  ## Return Types
287
451
 
288
452
  ### Query Mode
289
453
 
290
454
  ```typescript
455
+ // Basic usage
456
+ const result = useAPI((api) => api.posts.get());
457
+
458
+ // With options
459
+ const result = useAPI(
460
+ (api) => api.posts.get(),
461
+ { enabled: true } // Skip fetching when false
462
+ );
463
+
291
464
  type UseEnlaceQueryResult<TData, TError> = {
292
465
  loading: boolean; // No cached data and fetching
293
466
  fetching: boolean; // Request in progress
@@ -310,6 +483,19 @@ type UseEnlaceSelectorResult<TMethod> = {
310
483
  };
311
484
  ```
312
485
 
486
+ ### Request Options
487
+
488
+ ```typescript
489
+ type RequestOptions = {
490
+ query?: Record<string, unknown>; // Query parameters
491
+ body?: TBody; // Request body
492
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>); // Request headers
493
+ tags?: string[]; // Cache tags (GET only)
494
+ revalidateTags?: string[]; // Tags to invalidate after mutation
495
+ pathParams?: Record<string, string | number>; // Dynamic path parameters
496
+ };
497
+ ```
498
+
313
499
  ---
314
500
 
315
501
  ## Next.js Integration
@@ -432,6 +618,8 @@ type EnlaceHookOptions = {
432
618
  autoGenerateTags?: boolean; // default: true
433
619
  autoRevalidateTags?: boolean; // default: true
434
620
  staleTime?: number; // default: 0
621
+ onSuccess?: (payload: EnlaceCallbackPayload<unknown>) => void;
622
+ onError?: (payload: EnlaceErrorCallbackPayload<unknown>) => void;
435
623
  };
436
624
  ```
437
625
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { WildcardClient, EnlaceClient, EnlaceResponse, EnlaceOptions } from 'enlace-core';
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 */
@@ -11,6 +11,23 @@ type ReactRequestOptionsBase = {
11
11
  tags?: string[];
12
12
  /** Tags to invalidate after mutation (triggers refetch in matching queries) */
13
13
  revalidateTags?: string[];
14
+ /**
15
+ * Path parameters for dynamic URL segments.
16
+ * Used to replace :paramName placeholders in the URL path.
17
+ * @example
18
+ * // With path api.products[':id'].delete
19
+ * trigger({ pathParams: { id: '123' } }) // → DELETE /products/123
20
+ */
21
+ pathParams?: Record<string, string | number>;
22
+ };
23
+ /** Options for query mode hooks */
24
+ type UseEnlaceQueryOptions = {
25
+ /**
26
+ * Whether the query should execute.
27
+ * Set to false to skip fetching (useful when ID is "new" or undefined).
28
+ * @default true
29
+ */
30
+ enabled?: boolean;
14
31
  };
15
32
  type ApiClient<TSchema, TOptions = ReactRequestOptionsBase> = unknown extends TSchema ? WildcardClient<TOptions> : EnlaceClient<TSchema, TOptions>;
16
33
  type QueryFn<TSchema, TData, TError, TOptions = ReactRequestOptionsBase> = (api: ApiClient<TSchema, TOptions>) => Promise<EnlaceResponse<TData, TError>>;
@@ -55,7 +72,7 @@ type UseEnlaceSelectorResult<TMethod> = {
55
72
  loading: boolean;
56
73
  fetching: boolean;
57
74
  } & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
58
-
75
+ /** Options for createEnlaceHook factory */
59
76
  type EnlaceHookOptions = {
60
77
  /**
61
78
  * Auto-generate cache tags from URL path for GET requests.
@@ -67,11 +84,17 @@ type EnlaceHookOptions = {
67
84
  autoRevalidateTags?: boolean;
68
85
  /** Time in ms before cached data is considered stale. @default 0 (always stale) */
69
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;
70
91
  };
92
+ /** Hook type returned by createEnlaceHook */
71
93
  type EnlaceHook<TSchema> = {
72
94
  <TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: SelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
73
- <TData, TError>(queryFn: QueryFn<TSchema, TData, TError>): UseEnlaceQueryResult<TData, TError>;
95
+ <TData, TError>(queryFn: QueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
74
96
  };
97
+
75
98
  /**
76
99
  * Creates a React hook for making API calls.
77
100
  * Called at module level to create a reusable hook.
@@ -94,4 +117,4 @@ declare function onRevalidate(callback: Listener): () => void;
94
117
 
95
118
  declare function clearCache(key?: string): void;
96
119
 
97
- export { type ApiClient, type EnlaceHookOptions, HTTP_METHODS, type HookState, type QueryFn, type ReactRequestOptionsBase, type SelectorFn, type TrackedCall, 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 { WildcardClient, EnlaceClient, EnlaceResponse, EnlaceOptions } from 'enlace-core';
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 */
@@ -11,6 +11,23 @@ type ReactRequestOptionsBase = {
11
11
  tags?: string[];
12
12
  /** Tags to invalidate after mutation (triggers refetch in matching queries) */
13
13
  revalidateTags?: string[];
14
+ /**
15
+ * Path parameters for dynamic URL segments.
16
+ * Used to replace :paramName placeholders in the URL path.
17
+ * @example
18
+ * // With path api.products[':id'].delete
19
+ * trigger({ pathParams: { id: '123' } }) // → DELETE /products/123
20
+ */
21
+ pathParams?: Record<string, string | number>;
22
+ };
23
+ /** Options for query mode hooks */
24
+ type UseEnlaceQueryOptions = {
25
+ /**
26
+ * Whether the query should execute.
27
+ * Set to false to skip fetching (useful when ID is "new" or undefined).
28
+ * @default true
29
+ */
30
+ enabled?: boolean;
14
31
  };
15
32
  type ApiClient<TSchema, TOptions = ReactRequestOptionsBase> = unknown extends TSchema ? WildcardClient<TOptions> : EnlaceClient<TSchema, TOptions>;
16
33
  type QueryFn<TSchema, TData, TError, TOptions = ReactRequestOptionsBase> = (api: ApiClient<TSchema, TOptions>) => Promise<EnlaceResponse<TData, TError>>;
@@ -55,7 +72,7 @@ type UseEnlaceSelectorResult<TMethod> = {
55
72
  loading: boolean;
56
73
  fetching: boolean;
57
74
  } & HookResponseState<ExtractData<TMethod>, ExtractError<TMethod>>;
58
-
75
+ /** Options for createEnlaceHook factory */
59
76
  type EnlaceHookOptions = {
60
77
  /**
61
78
  * Auto-generate cache tags from URL path for GET requests.
@@ -67,11 +84,17 @@ type EnlaceHookOptions = {
67
84
  autoRevalidateTags?: boolean;
68
85
  /** Time in ms before cached data is considered stale. @default 0 (always stale) */
69
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;
70
91
  };
92
+ /** Hook type returned by createEnlaceHook */
71
93
  type EnlaceHook<TSchema> = {
72
94
  <TMethod extends (...args: any[]) => Promise<EnlaceResponse<unknown, unknown>>>(selector: SelectorFn<TSchema, TMethod>): UseEnlaceSelectorResult<TMethod>;
73
- <TData, TError>(queryFn: QueryFn<TSchema, TData, TError>): UseEnlaceQueryResult<TData, TError>;
95
+ <TData, TError>(queryFn: QueryFn<TSchema, TData, TError>, options?: UseEnlaceQueryOptions): UseEnlaceQueryResult<TData, TError>;
74
96
  };
97
+
75
98
  /**
76
99
  * Creates a React hook for making API calls.
77
100
  * Called at module level to create a reusable hook.
@@ -94,4 +117,4 @@ declare function onRevalidate(callback: Listener): () => void;
94
117
 
95
118
  declare function clearCache(key?: string): void;
96
119
 
97
- export { type ApiClient, type EnlaceHookOptions, HTTP_METHODS, type HookState, type QueryFn, type ReactRequestOptionsBase, type SelectorFn, type TrackedCall, 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,
@@ -184,11 +184,29 @@ function onRevalidate(callback) {
184
184
  }
185
185
 
186
186
  // src/react/useQueryMode.ts
187
+ function resolvePath(path, pathParams) {
188
+ if (!pathParams) return path;
189
+ return path.map((segment) => {
190
+ if (segment.startsWith(":")) {
191
+ const paramName = segment.slice(1);
192
+ const value = pathParams[paramName];
193
+ if (value === void 0) {
194
+ throw new Error(`Missing path parameter: ${paramName}`);
195
+ }
196
+ return String(value);
197
+ }
198
+ return segment;
199
+ });
200
+ }
187
201
  function useQueryMode(api, trackedCall, options) {
188
- const { autoGenerateTags, staleTime } = options;
202
+ const { autoGenerateTags, staleTime, enabled } = options;
189
203
  const queryKey = createQueryKey(trackedCall);
190
204
  const requestOptions = trackedCall.options;
191
- const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(trackedCall.path) : []);
205
+ const resolvedPath = resolvePath(
206
+ trackedCall.path,
207
+ requestOptions?.pathParams
208
+ );
209
+ const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(resolvedPath) : []);
192
210
  const getCacheState = (includeNeedsFetch = false) => {
193
211
  const cached = getCache(queryKey);
194
212
  const hasCachedData = cached?.data !== void 0;
@@ -202,17 +220,22 @@ function useQueryMode(api, trackedCall, options) {
202
220
  error: cached?.error
203
221
  };
204
222
  };
205
- const [state, dispatch] = (0, import_react.useReducer)(hookReducer, null, () => getCacheState(true));
223
+ const [state, dispatch] = (0, import_react.useReducer)(
224
+ hookReducer,
225
+ null,
226
+ () => getCacheState(true)
227
+ );
206
228
  const mountedRef = (0, import_react.useRef)(true);
207
229
  const fetchRef = (0, import_react.useRef)(null);
208
230
  (0, import_react.useEffect)(() => {
209
231
  mountedRef.current = true;
232
+ if (!enabled) {
233
+ dispatch({ type: "RESET" });
234
+ return () => {
235
+ mountedRef.current = false;
236
+ };
237
+ }
210
238
  dispatch({ type: "RESET", state: getCacheState(true) });
211
- const unsubscribe = subscribeCache(queryKey, () => {
212
- if (mountedRef.current) {
213
- dispatch({ type: "SYNC_CACHE", state: getCacheState() });
214
- }
215
- });
216
239
  const doFetch = () => {
217
240
  const cached2 = getCache(queryKey);
218
241
  if (cached2?.promise) {
@@ -220,7 +243,7 @@ function useQueryMode(api, trackedCall, options) {
220
243
  }
221
244
  dispatch({ type: "FETCH_START" });
222
245
  let current = api;
223
- for (const segment of trackedCall.path) {
246
+ for (const segment of resolvedPath) {
224
247
  current = current[segment];
225
248
  }
226
249
  const method = current[trackedCall.method];
@@ -228,7 +251,7 @@ function useQueryMode(api, trackedCall, options) {
228
251
  if (mountedRef.current) {
229
252
  setCache(queryKey, {
230
253
  data: res.ok ? res.data : void 0,
231
- error: res.ok ? void 0 : res.error,
254
+ error: res.ok || res.status === 0 ? void 0 : res.error,
232
255
  ok: res.ok,
233
256
  timestamp: Date.now(),
234
257
  tags: queryTags
@@ -247,12 +270,17 @@ function useQueryMode(api, trackedCall, options) {
247
270
  } else {
248
271
  doFetch();
249
272
  }
273
+ const unsubscribe = subscribeCache(queryKey, () => {
274
+ if (mountedRef.current) {
275
+ dispatch({ type: "SYNC_CACHE", state: getCacheState() });
276
+ }
277
+ });
250
278
  return () => {
251
279
  mountedRef.current = false;
252
280
  fetchRef.current = null;
253
281
  unsubscribe();
254
282
  };
255
- }, [queryKey]);
283
+ }, [queryKey, enabled]);
256
284
  (0, import_react.useEffect)(() => {
257
285
  if (queryTags.length === 0) return;
258
286
  return onRevalidate((invalidatedTags) => {
@@ -299,23 +327,56 @@ function createTrackingProxy(onTrack) {
299
327
 
300
328
  // src/react/useSelectorMode.ts
301
329
  var import_react2 = require("react");
302
- function useSelectorMode(method, path, autoRevalidateTags) {
330
+ function resolvePath2(path, pathParams) {
331
+ if (!pathParams) return path;
332
+ return path.map((segment) => {
333
+ if (segment.startsWith(":")) {
334
+ const paramName = segment.slice(1);
335
+ const value = pathParams[paramName];
336
+ if (value === void 0) {
337
+ throw new Error(`Missing path parameter: ${paramName}`);
338
+ }
339
+ return String(value);
340
+ }
341
+ return segment;
342
+ });
343
+ }
344
+ function hasPathParams(path) {
345
+ return path.some((segment) => segment.startsWith(":"));
346
+ }
347
+ function useSelectorMode(config) {
348
+ const { method, api, path, methodName, autoRevalidateTags } = config;
303
349
  const [state, dispatch] = (0, import_react2.useReducer)(hookReducer, initialState);
304
350
  const methodRef = (0, import_react2.useRef)(method);
351
+ const apiRef = (0, import_react2.useRef)(api);
305
352
  const triggerRef = (0, import_react2.useRef)(null);
306
353
  const pathRef = (0, import_react2.useRef)(path);
354
+ const methodNameRef = (0, import_react2.useRef)(methodName);
307
355
  const autoRevalidateRef = (0, import_react2.useRef)(autoRevalidateTags);
308
356
  methodRef.current = method;
357
+ apiRef.current = api;
309
358
  pathRef.current = path;
359
+ methodNameRef.current = methodName;
310
360
  autoRevalidateRef.current = autoRevalidateTags;
311
361
  if (!triggerRef.current) {
312
362
  triggerRef.current = (async (...args) => {
313
363
  dispatch({ type: "FETCH_START" });
314
- const res = await methodRef.current(...args);
364
+ const options = args[0];
365
+ const resolvedPath = resolvePath2(pathRef.current, options?.pathParams);
366
+ let res;
367
+ if (hasPathParams(pathRef.current)) {
368
+ let current = apiRef.current;
369
+ for (const segment of resolvedPath) {
370
+ current = current[segment];
371
+ }
372
+ const resolvedMethod = current[methodNameRef.current];
373
+ res = await resolvedMethod(...args);
374
+ } else {
375
+ res = await methodRef.current(...args);
376
+ }
315
377
  if (res.ok) {
316
378
  dispatch({ type: "FETCH_SUCCESS", data: res.data });
317
- const options = args[0];
318
- const tagsToInvalidate = options?.revalidateTags ?? (autoRevalidateRef.current ? generateTags(pathRef.current) : []);
379
+ const tagsToInvalidate = options?.revalidateTags ?? (autoRevalidateRef.current ? generateTags(resolvedPath) : []);
319
380
  if (tagsToInvalidate.length > 0) {
320
381
  invalidateTags(tagsToInvalidate);
321
382
  }
@@ -333,13 +394,15 @@ function useSelectorMode(method, path, autoRevalidateTags) {
333
394
 
334
395
  // src/react/createEnlaceHook.ts
335
396
  function createEnlaceHook(baseUrl, defaultOptions = {}, hookOptions = {}) {
336
- const api = (0, import_enlace_core.createEnlace)(baseUrl, defaultOptions);
337
397
  const {
338
398
  autoGenerateTags = true,
339
399
  autoRevalidateTags = true,
340
- staleTime = 0
400
+ staleTime = 0,
401
+ onSuccess,
402
+ onError
341
403
  } = hookOptions;
342
- function useEnlaceHook(selectorOrQuery) {
404
+ const api = (0, import_enlace_core.createEnlace)(baseUrl, defaultOptions, { onSuccess, onError });
405
+ function useEnlaceHook(selectorOrQuery, queryOptions) {
343
406
  let trackingResult = {
344
407
  trackedCall: null,
345
408
  selectorPath: null,
@@ -353,16 +416,18 @@ function createEnlaceHook(baseUrl, defaultOptions = {}, hookOptions = {}) {
353
416
  );
354
417
  if (typeof result === "function") {
355
418
  const actualResult = selectorOrQuery(api);
356
- return useSelectorMode(
357
- actualResult,
358
- trackingResult.selectorPath ?? [],
419
+ return useSelectorMode({
420
+ method: actualResult,
421
+ api,
422
+ path: trackingResult.selectorPath ?? [],
423
+ methodName: trackingResult.selectorMethod ?? "",
359
424
  autoRevalidateTags
360
- );
425
+ });
361
426
  }
362
427
  return useQueryMode(
363
428
  api,
364
429
  trackingResult.trackedCall,
365
- { autoGenerateTags, staleTime }
430
+ { autoGenerateTags, staleTime, enabled: queryOptions?.enabled ?? true }
366
431
  );
367
432
  }
368
433
  return useEnlaceHook;