enlace 0.0.1-beta.1 → 0.0.1-beta.10

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.
@@ -17,107 +17,20 @@ var __copyProps = (to, from, except, desc) => {
17
17
  }
18
18
  return to;
19
19
  };
20
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
21
20
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
21
 
23
- // src/next/createEnlaceHook.ts
24
- var createEnlaceHook_exports = {};
25
- __export(createEnlaceHook_exports, {
26
- createEnlaceHook: () => createEnlaceHook
22
+ // src/hook/index.ts
23
+ var hook_exports = {};
24
+ __export(hook_exports, {
25
+ HTTP_METHODS: () => HTTP_METHODS,
26
+ createEnlaceHookNext: () => createEnlaceHookNext,
27
+ createEnlaceHookReact: () => createEnlaceHookReact
27
28
  });
28
- module.exports = __toCommonJS(createEnlaceHook_exports);
29
+ module.exports = __toCommonJS(hook_exports);
29
30
 
30
- // src/next/index.ts
31
- var next_exports = {};
32
- __export(next_exports, {
33
- createEnlace: () => createEnlace
34
- });
35
- var import_enlace_core2 = require("enlace-core");
36
-
37
- // src/next/fetch.ts
31
+ // src/react/createEnlaceHookReact.ts
38
32
  var import_enlace_core = require("enlace-core");
39
33
 
40
- // src/utils/generateTags.ts
41
- function generateTags(path) {
42
- return path.map((_, i) => path.slice(0, i + 1).join("/"));
43
- }
44
-
45
- // src/next/fetch.ts
46
- async function executeNextFetch(baseUrl, path, method, combinedOptions, requestOptions) {
47
- const {
48
- autoGenerateTags = true,
49
- autoRevalidateTags = true,
50
- revalidator,
51
- headers: defaultHeaders,
52
- ...restOptions
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
- const isGet = method === "GET";
57
- const autoTags = generateTags(path);
58
- const fetchOptions = {
59
- ...restOptions,
60
- method
61
- };
62
- if (requestOptions?.cache) {
63
- fetchOptions.cache = requestOptions.cache;
64
- }
65
- if (isGet) {
66
- const tags = requestOptions?.tags ?? (autoGenerateTags ? autoTags : void 0);
67
- const nextFetchOptions = {};
68
- if (tags) {
69
- nextFetchOptions.tags = tags;
70
- }
71
- if (requestOptions?.revalidate !== void 0) {
72
- nextFetchOptions.revalidate = requestOptions.revalidate;
73
- }
74
- fetchOptions.next = nextFetchOptions;
75
- }
76
- if (headers) {
77
- fetchOptions.headers = headers;
78
- }
79
- if (requestOptions?.body !== void 0) {
80
- if ((0, import_enlace_core.isJsonBody)(requestOptions.body)) {
81
- fetchOptions.body = JSON.stringify(requestOptions.body);
82
- headers = (0, import_enlace_core.mergeHeaders)(headers, { "Content-Type": "application/json" });
83
- if (headers) {
84
- fetchOptions.headers = headers;
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
- };
112
- }
113
-
114
- // src/next/index.ts
115
- __reExport(next_exports, require("enlace-core"));
116
- function createEnlace(baseUrl, defaultOptions = {}, nextOptions = {}) {
117
- const combinedOptions = { ...defaultOptions, ...nextOptions };
118
- return (0, import_enlace_core2.createProxyHandler)(baseUrl, combinedOptions, [], executeNextFetch);
119
- }
120
-
121
34
  // src/react/useQueryMode.ts
122
35
  var import_react = require("react");
123
36
 
@@ -125,14 +38,13 @@ var import_react = require("react");
125
38
  var initialState = {
126
39
  loading: false,
127
40
  fetching: false,
128
- ok: void 0,
129
41
  data: void 0,
130
42
  error: void 0
131
43
  };
132
44
  function hookReducer(state, action) {
133
45
  switch (action.type) {
134
46
  case "RESET":
135
- return action.state;
47
+ return action.state ?? initialState;
136
48
  case "FETCH_START":
137
49
  return {
138
50
  ...state,
@@ -143,7 +55,6 @@ function hookReducer(state, action) {
143
55
  return {
144
56
  loading: false,
145
57
  fetching: false,
146
- ok: true,
147
58
  data: action.data,
148
59
  error: void 0
149
60
  };
@@ -151,7 +62,6 @@ function hookReducer(state, action) {
151
62
  return {
152
63
  loading: false,
153
64
  fetching: false,
154
- ok: false,
155
65
  data: void 0,
156
66
  error: action.error
157
67
  };
@@ -162,6 +72,11 @@ function hookReducer(state, action) {
162
72
  }
163
73
  }
164
74
 
75
+ // src/utils/generateTags.ts
76
+ function generateTags(path) {
77
+ return path.map((_, i) => path.slice(0, i + 1).join("/"));
78
+ }
79
+
165
80
  // src/utils/sortObjectKeys.ts
166
81
  function sortObjectKeys(obj) {
167
82
  if (obj === null || typeof obj !== "object") return obj;
@@ -192,7 +107,7 @@ function getCache(key) {
192
107
  function setCache(key, entry) {
193
108
  const existing = cache.get(key);
194
109
  if (existing) {
195
- if ("ok" in entry) {
110
+ if ("data" in entry || "error" in entry) {
196
111
  delete existing.promise;
197
112
  }
198
113
  Object.assign(existing, entry);
@@ -201,7 +116,6 @@ function setCache(key, entry) {
201
116
  cache.set(key, {
202
117
  data: void 0,
203
118
  error: void 0,
204
- ok: void 0,
205
119
  timestamp: 0,
206
120
  tags: [],
207
121
  subscribers: /* @__PURE__ */ new Set(),
@@ -215,7 +129,6 @@ function subscribeCache(key, callback) {
215
129
  cache.set(key, {
216
130
  data: void 0,
217
131
  error: void 0,
218
- ok: void 0,
219
132
  timestamp: 0,
220
133
  tags: [],
221
134
  subscribers: /* @__PURE__ */ new Set()
@@ -238,7 +151,6 @@ function clearCacheByTags(tags) {
238
151
  if (hasMatch) {
239
152
  entry.data = void 0;
240
153
  entry.error = void 0;
241
- entry.ok = void 0;
242
154
  entry.timestamp = 0;
243
155
  delete entry.promise;
244
156
  }
@@ -257,11 +169,29 @@ function onRevalidate(callback) {
257
169
  }
258
170
 
259
171
  // src/react/useQueryMode.ts
172
+ function resolvePath(path, pathParams) {
173
+ if (!pathParams) return path;
174
+ return path.map((segment) => {
175
+ if (segment.startsWith(":")) {
176
+ const paramName = segment.slice(1);
177
+ const value = pathParams[paramName];
178
+ if (value === void 0) {
179
+ throw new Error(`Missing path parameter: ${paramName}`);
180
+ }
181
+ return String(value);
182
+ }
183
+ return segment;
184
+ });
185
+ }
260
186
  function useQueryMode(api, trackedCall, options) {
261
- const { autoGenerateTags, staleTime } = options;
187
+ const { autoGenerateTags, staleTime, enabled } = options;
262
188
  const queryKey = createQueryKey(trackedCall);
263
189
  const requestOptions = trackedCall.options;
264
- const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(trackedCall.path) : []);
190
+ const resolvedPath = resolvePath(
191
+ trackedCall.path,
192
+ requestOptions?.pathParams
193
+ );
194
+ const queryTags = requestOptions?.tags ?? (autoGenerateTags ? generateTags(resolvedPath) : []);
265
195
  const getCacheState = (includeNeedsFetch = false) => {
266
196
  const cached = getCache(queryKey);
267
197
  const hasCachedData = cached?.data !== void 0;
@@ -270,22 +200,26 @@ function useQueryMode(api, trackedCall, options) {
270
200
  return {
271
201
  loading: !hasCachedData && (isFetching || needsFetch),
272
202
  fetching: isFetching || needsFetch,
273
- ok: cached?.ok,
274
203
  data: cached?.data,
275
204
  error: cached?.error
276
205
  };
277
206
  };
278
- const [state, dispatch] = (0, import_react.useReducer)(hookReducer, null, () => getCacheState(true));
207
+ const [state, dispatch] = (0, import_react.useReducer)(
208
+ hookReducer,
209
+ null,
210
+ () => getCacheState(true)
211
+ );
279
212
  const mountedRef = (0, import_react.useRef)(true);
280
213
  const fetchRef = (0, import_react.useRef)(null);
281
214
  (0, import_react.useEffect)(() => {
282
215
  mountedRef.current = true;
216
+ if (!enabled) {
217
+ dispatch({ type: "RESET" });
218
+ return () => {
219
+ mountedRef.current = false;
220
+ };
221
+ }
283
222
  dispatch({ type: "RESET", state: getCacheState(true) });
284
- const unsubscribe = subscribeCache(queryKey, () => {
285
- if (mountedRef.current) {
286
- dispatch({ type: "SYNC_CACHE", state: getCacheState() });
287
- }
288
- });
289
223
  const doFetch = () => {
290
224
  const cached2 = getCache(queryKey);
291
225
  if (cached2?.promise) {
@@ -293,16 +227,15 @@ function useQueryMode(api, trackedCall, options) {
293
227
  }
294
228
  dispatch({ type: "FETCH_START" });
295
229
  let current = api;
296
- for (const segment of trackedCall.path) {
230
+ for (const segment of resolvedPath) {
297
231
  current = current[segment];
298
232
  }
299
233
  const method = current[trackedCall.method];
300
234
  const fetchPromise = method(trackedCall.options).then((res) => {
301
235
  if (mountedRef.current) {
302
236
  setCache(queryKey, {
303
- data: res.ok ? res.data : void 0,
304
- error: res.ok ? void 0 : res.error,
305
- ok: res.ok,
237
+ data: res.error ? void 0 : res.data,
238
+ error: res.error,
306
239
  timestamp: Date.now(),
307
240
  tags: queryTags
308
241
  });
@@ -320,12 +253,17 @@ function useQueryMode(api, trackedCall, options) {
320
253
  } else {
321
254
  doFetch();
322
255
  }
256
+ const unsubscribe = subscribeCache(queryKey, () => {
257
+ if (mountedRef.current) {
258
+ dispatch({ type: "SYNC_CACHE", state: getCacheState() });
259
+ }
260
+ });
323
261
  return () => {
324
262
  mountedRef.current = false;
325
263
  fetchRef.current = null;
326
264
  unsubscribe();
327
265
  };
328
- }, [queryKey]);
266
+ }, [queryKey, enabled]);
329
267
  (0, import_react.useEffect)(() => {
330
268
  if (queryTags.length === 0) return;
331
269
  return onRevalidate((invalidatedTags) => {
@@ -338,25 +276,90 @@ function useQueryMode(api, trackedCall, options) {
338
276
  return state;
339
277
  }
340
278
 
279
+ // src/react/types.ts
280
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
281
+
282
+ // src/react/trackingProxy.ts
283
+ function createTrackingProxy(onTrack) {
284
+ const createProxy = (path = []) => {
285
+ return new Proxy(() => {
286
+ }, {
287
+ get(_, prop) {
288
+ if (HTTP_METHODS.includes(prop)) {
289
+ const methodFn = (options) => {
290
+ onTrack({
291
+ trackedCall: { path, method: prop, options },
292
+ selectorPath: null,
293
+ selectorMethod: null
294
+ });
295
+ return Promise.resolve({ status: 200, data: void 0, error: void 0 });
296
+ };
297
+ onTrack({
298
+ trackedCall: null,
299
+ selectorPath: path,
300
+ selectorMethod: prop
301
+ });
302
+ return methodFn;
303
+ }
304
+ return createProxy([...path, prop]);
305
+ }
306
+ });
307
+ };
308
+ return createProxy();
309
+ }
310
+
341
311
  // src/react/useSelectorMode.ts
342
312
  var import_react2 = require("react");
343
- function useSelectorMode(method, path, autoRevalidateTags) {
313
+ function resolvePath2(path, pathParams) {
314
+ if (!pathParams) return path;
315
+ return path.map((segment) => {
316
+ if (segment.startsWith(":")) {
317
+ const paramName = segment.slice(1);
318
+ const value = pathParams[paramName];
319
+ if (value === void 0) {
320
+ throw new Error(`Missing path parameter: ${paramName}`);
321
+ }
322
+ return String(value);
323
+ }
324
+ return segment;
325
+ });
326
+ }
327
+ function hasPathParams(path) {
328
+ return path.some((segment) => segment.startsWith(":"));
329
+ }
330
+ function useSelectorMode(config) {
331
+ const { method, api, path, methodName, autoRevalidateTags } = config;
344
332
  const [state, dispatch] = (0, import_react2.useReducer)(hookReducer, initialState);
345
333
  const methodRef = (0, import_react2.useRef)(method);
334
+ const apiRef = (0, import_react2.useRef)(api);
346
335
  const triggerRef = (0, import_react2.useRef)(null);
347
336
  const pathRef = (0, import_react2.useRef)(path);
337
+ const methodNameRef = (0, import_react2.useRef)(methodName);
348
338
  const autoRevalidateRef = (0, import_react2.useRef)(autoRevalidateTags);
349
339
  methodRef.current = method;
340
+ apiRef.current = api;
350
341
  pathRef.current = path;
342
+ methodNameRef.current = methodName;
351
343
  autoRevalidateRef.current = autoRevalidateTags;
352
344
  if (!triggerRef.current) {
353
345
  triggerRef.current = (async (...args) => {
354
346
  dispatch({ type: "FETCH_START" });
355
- const res = await methodRef.current(...args);
356
- if (res.ok) {
347
+ const options = args[0];
348
+ const resolvedPath = resolvePath2(pathRef.current, options?.pathParams);
349
+ let res;
350
+ if (hasPathParams(pathRef.current)) {
351
+ let current = apiRef.current;
352
+ for (const segment of resolvedPath) {
353
+ current = current[segment];
354
+ }
355
+ const resolvedMethod = current[methodNameRef.current];
356
+ res = await resolvedMethod(...args);
357
+ } else {
358
+ res = await methodRef.current(...args);
359
+ }
360
+ if (!res.error) {
357
361
  dispatch({ type: "FETCH_SUCCESS", data: res.data });
358
- const options = args[0];
359
- const tagsToInvalidate = options?.revalidateTags ?? (autoRevalidateRef.current ? generateTags(pathRef.current) : []);
362
+ const tagsToInvalidate = options?.revalidateTags ?? (autoRevalidateRef.current ? generateTags(resolvedPath) : []);
360
363
  if (tagsToInvalidate.length > 0) {
361
364
  invalidateTags(tagsToInvalidate);
362
365
  }
@@ -372,71 +375,141 @@ function useSelectorMode(method, path, autoRevalidateTags) {
372
375
  };
373
376
  }
374
377
 
375
- // src/react/types.ts
376
- var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
378
+ // src/react/createEnlaceHookReact.ts
379
+ function createEnlaceHookReact(baseUrl, defaultOptions = {}, hookOptions = {}) {
380
+ const {
381
+ autoGenerateTags = true,
382
+ autoRevalidateTags = true,
383
+ staleTime = 0,
384
+ onSuccess,
385
+ onError
386
+ } = hookOptions;
387
+ const api = (0, import_enlace_core.createEnlace)(baseUrl, defaultOptions, { onSuccess, onError });
388
+ function useEnlaceHook(selectorOrQuery, queryOptions) {
389
+ let trackingResult = {
390
+ trackedCall: null,
391
+ selectorPath: null,
392
+ selectorMethod: null
393
+ };
394
+ const trackingProxy = createTrackingProxy((result2) => {
395
+ trackingResult = result2;
396
+ });
397
+ const result = selectorOrQuery(
398
+ trackingProxy
399
+ );
400
+ if (typeof result === "function") {
401
+ const actualResult = selectorOrQuery(api);
402
+ return useSelectorMode({
403
+ method: actualResult,
404
+ api,
405
+ path: trackingResult.selectorPath ?? [],
406
+ methodName: trackingResult.selectorMethod ?? "",
407
+ autoRevalidateTags
408
+ });
409
+ }
410
+ return useQueryMode(
411
+ api,
412
+ trackingResult.trackedCall,
413
+ { autoGenerateTags, staleTime, enabled: queryOptions?.enabled ?? true }
414
+ );
415
+ }
416
+ return useEnlaceHook;
417
+ }
377
418
 
378
- // src/react/trackingProxy.ts
379
- function createTrackingProxy(onTrack) {
380
- const createProxy = (path = []) => {
381
- return new Proxy(() => {
382
- }, {
383
- get(_, prop) {
384
- if (HTTP_METHODS.includes(prop)) {
385
- const methodFn = (options) => {
386
- onTrack({
387
- trackedCall: { path, method: prop, options },
388
- selectorPath: null,
389
- selectorMethod: null
390
- });
391
- return Promise.resolve({ ok: true, data: void 0 });
392
- };
393
- onTrack({
394
- trackedCall: null,
395
- selectorPath: path,
396
- selectorMethod: prop
397
- });
398
- return methodFn;
399
- }
400
- return createProxy([...path, prop]);
419
+ // src/next/index.ts
420
+ var import_enlace_core3 = require("enlace-core");
421
+
422
+ // src/next/fetch.ts
423
+ var import_enlace_core2 = require("enlace-core");
424
+ async function executeNextFetch(baseUrl, path, method, combinedOptions, requestOptions) {
425
+ const {
426
+ autoGenerateTags = true,
427
+ autoRevalidateTags = true,
428
+ revalidator,
429
+ onSuccess,
430
+ ...coreOptions
431
+ } = combinedOptions;
432
+ const isGet = method === "GET";
433
+ const autoTags = generateTags(path);
434
+ const nextOnSuccess = (payload) => {
435
+ if (!isGet && !requestOptions?.skipRevalidator) {
436
+ const revalidateTags = requestOptions?.revalidateTags ?? (autoRevalidateTags ? autoTags : []);
437
+ const revalidatePaths = requestOptions?.revalidatePaths ?? [];
438
+ if (revalidateTags.length || revalidatePaths.length) {
439
+ revalidator?.(revalidateTags, revalidatePaths);
401
440
  }
402
- });
441
+ }
442
+ onSuccess?.(payload);
403
443
  };
404
- return createProxy();
444
+ const nextRequestOptions = { ...requestOptions };
445
+ if (isGet) {
446
+ const tags = requestOptions?.tags ?? (autoGenerateTags ? autoTags : void 0);
447
+ const nextFetchOptions = {};
448
+ if (tags) {
449
+ nextFetchOptions.tags = tags;
450
+ }
451
+ if (requestOptions?.revalidate !== void 0) {
452
+ nextFetchOptions.revalidate = requestOptions.revalidate;
453
+ }
454
+ nextRequestOptions.next = nextFetchOptions;
455
+ }
456
+ return (0, import_enlace_core2.executeFetch)(
457
+ baseUrl,
458
+ path,
459
+ method,
460
+ { ...coreOptions, onSuccess: nextOnSuccess },
461
+ nextRequestOptions
462
+ );
405
463
  }
406
464
 
407
- // src/next/createEnlaceHook.ts
408
- function createEnlaceHook(baseUrl, defaultOptions = {}, hookOptions = {}) {
465
+ // src/next/index.ts
466
+ function createEnlaceNext(baseUrl, defaultOptions = {}, nextOptions = {}) {
467
+ const combinedOptions = { ...defaultOptions, ...nextOptions };
468
+ return (0, import_enlace_core3.createProxyHandler)(
469
+ baseUrl,
470
+ combinedOptions,
471
+ [],
472
+ executeNextFetch
473
+ );
474
+ }
475
+
476
+ // src/next/createEnlaceHookNext.ts
477
+ function createEnlaceHookNext(baseUrl, defaultOptions = {}, hookOptions = {}) {
409
478
  const {
410
479
  autoGenerateTags = true,
411
480
  autoRevalidateTags = true,
412
481
  staleTime = 0,
413
482
  ...nextOptions
414
483
  } = hookOptions;
415
- const api = createEnlace(baseUrl, defaultOptions, {
484
+ const api = createEnlaceNext(baseUrl, defaultOptions, {
416
485
  autoGenerateTags,
417
486
  autoRevalidateTags,
418
487
  ...nextOptions
419
488
  });
420
- function useEnlaceHook(selectorOrQuery) {
489
+ function useEnlaceHook(selectorOrQuery, queryOptions) {
421
490
  let trackedCall = null;
422
491
  let selectorPath = null;
492
+ let selectorMethod = null;
423
493
  const trackingProxy = createTrackingProxy((result2) => {
424
494
  trackedCall = result2.trackedCall;
425
495
  selectorPath = result2.selectorPath;
496
+ selectorMethod = result2.selectorMethod;
426
497
  });
427
498
  const result = selectorOrQuery(trackingProxy);
428
499
  if (typeof result === "function") {
429
500
  const actualResult = selectorOrQuery(api);
430
- return useSelectorMode(
431
- actualResult,
432
- selectorPath ?? [],
501
+ return useSelectorMode({
502
+ method: actualResult,
503
+ api,
504
+ path: selectorPath ?? [],
505
+ methodName: selectorMethod ?? "",
433
506
  autoRevalidateTags
434
- );
507
+ });
435
508
  }
436
509
  return useQueryMode(
437
510
  api,
438
511
  trackedCall,
439
- { autoGenerateTags, staleTime }
512
+ { autoGenerateTags, staleTime, enabled: queryOptions?.enabled ?? true }
440
513
  );
441
514
  }
442
515
  return useEnlaceHook;