@igniter-js/caller 0.1.4 → 0.1.51

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.
@@ -0,0 +1,679 @@
1
+ import { createContext, useState, useMemo, useCallback, useContext, useRef, useEffect } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ // src/client/builders/provider.builder.tsx
5
+
6
+ // src/client/utils/cache.ts
7
+ var _IgniterCallerClientCache = class _IgniterCallerClientCache {
8
+ static get(key, staleTime) {
9
+ const entry = _IgniterCallerClientCache.cache.get(key);
10
+ if (!entry) return void 0;
11
+ if (staleTime && Date.now() - entry.timestamp > staleTime) {
12
+ _IgniterCallerClientCache.cache.delete(key);
13
+ return void 0;
14
+ }
15
+ return entry.data;
16
+ }
17
+ static set(key, data) {
18
+ _IgniterCallerClientCache.cache.set(key, {
19
+ data,
20
+ timestamp: Date.now()
21
+ });
22
+ }
23
+ static replacePrefix(prefix, data) {
24
+ for (const [key, entry] of _IgniterCallerClientCache.cache.entries()) {
25
+ if (key.startsWith(prefix)) {
26
+ _IgniterCallerClientCache.cache.set(key, {
27
+ data,
28
+ timestamp: entry.timestamp
29
+ });
30
+ }
31
+ }
32
+ }
33
+ static clearPrefix(prefix) {
34
+ for (const key of _IgniterCallerClientCache.cache.keys()) {
35
+ if (key.startsWith(prefix)) {
36
+ _IgniterCallerClientCache.cache.delete(key);
37
+ }
38
+ }
39
+ }
40
+ };
41
+ _IgniterCallerClientCache.cache = /* @__PURE__ */ new Map();
42
+ var IgniterCallerClientCache = _IgniterCallerClientCache;
43
+
44
+ // src/client/utils/query-key.ts
45
+ var EMPTY_KEY = "{}";
46
+ function stableStringify(value) {
47
+ if (value === null || value === void 0) return EMPTY_KEY;
48
+ if (typeof value !== "object") return JSON.stringify(value);
49
+ if (Array.isArray(value)) {
50
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
51
+ }
52
+ const entries = Object.entries(value).sort(
53
+ ([a], [b]) => a.localeCompare(b)
54
+ );
55
+ return `{${entries.map(([key, val]) => `${JSON.stringify(key)}:${stableStringify(val)}`).join(",")}}`;
56
+ }
57
+ function buildQueryKey(params) {
58
+ const { callerKey, method, path, query, params: pathParams } = params;
59
+ const payload = {
60
+ params: pathParams || {},
61
+ query: query || {}
62
+ };
63
+ return `${callerKey}::${method.toUpperCase()}::${path}::${stableStringify(payload)}`;
64
+ }
65
+ function buildQueryPrefix(params) {
66
+ const { callerKey, method, path } = params;
67
+ return `${callerKey}::${method.toUpperCase()}::${path}`;
68
+ }
69
+
70
+ // src/client/utils/cookies.ts
71
+ function buildCookieHeader(cookies) {
72
+ if (!cookies || Object.keys(cookies).length === 0) return void 0;
73
+ return Object.entries(cookies).map(([key, value]) => `${key}=${value}`).join("; ");
74
+ }
75
+
76
+ // src/client/builders/hooks.builder.tsx
77
+ function mergeQueryInput(config, options, variables) {
78
+ const headers = {
79
+ ...config.headers || {},
80
+ ...options?.headers || {},
81
+ ...variables?.headers || {}
82
+ };
83
+ const cookies = {
84
+ ...config.cookies || {},
85
+ ...options?.cookies || {},
86
+ ...variables?.cookies || {}
87
+ };
88
+ const query = {
89
+ ...config.query || {},
90
+ ...options?.query || {},
91
+ ...variables?.query || {},
92
+ ...options?.params || {},
93
+ ...variables?.params || {}
94
+ };
95
+ const cookieHeader = buildCookieHeader(cookies);
96
+ if (cookieHeader && !headers.Cookie) {
97
+ headers.Cookie = cookieHeader;
98
+ }
99
+ return {
100
+ headers,
101
+ cookies,
102
+ query,
103
+ params: variables?.params || options?.params
104
+ };
105
+ }
106
+ function mergeMutateInput(config, options, variables) {
107
+ const merged = mergeQueryInput(config, options, variables);
108
+ return {
109
+ ...merged,
110
+ body: variables?.body ?? options?.body
111
+ };
112
+ }
113
+ function createUseQuery(params) {
114
+ const {
115
+ callerKey,
116
+ method,
117
+ path,
118
+ buildRequest,
119
+ register,
120
+ unregister,
121
+ invalidate,
122
+ getMergedConfig,
123
+ onError
124
+ } = params;
125
+ return (options) => {
126
+ const [status, setStatus] = useState(
127
+ options?.initialData ? "success" : "loading"
128
+ );
129
+ const [isFetching, setIsFetching] = useState(false);
130
+ const [response, setResponse] = useState({
131
+ data: options?.initialData ?? null,
132
+ error: null
133
+ });
134
+ const [variables, setVariables] = useState(
135
+ void 0
136
+ );
137
+ const optionsRef = useRef(options);
138
+ optionsRef.current = options;
139
+ const lastVariablesRef = useRef(void 0);
140
+ const stableKey = useMemo(() => {
141
+ const mergedConfig = getMergedConfig();
142
+ const mergedInput = mergeQueryInput(
143
+ mergedConfig,
144
+ options,
145
+ lastVariablesRef.current
146
+ );
147
+ return buildQueryKey({
148
+ callerKey,
149
+ method,
150
+ path,
151
+ query: mergedInput.query,
152
+ params: mergedInput.params
153
+ });
154
+ }, [
155
+ callerKey,
156
+ method,
157
+ path,
158
+ getMergedConfig,
159
+ JSON.stringify(options?.params || {}),
160
+ JSON.stringify(options?.query || {})
161
+ ]);
162
+ const execute = useCallback(
163
+ async (nextVariables) => {
164
+ if (optionsRef.current?.enabled === false) return;
165
+ const mergedConfig = getMergedConfig();
166
+ const mergedInput = mergeQueryInput(
167
+ mergedConfig,
168
+ optionsRef.current,
169
+ nextVariables
170
+ );
171
+ const queryKey = buildQueryKey({
172
+ callerKey,
173
+ method,
174
+ path,
175
+ query: mergedInput.query,
176
+ params: mergedInput.params
177
+ });
178
+ lastVariablesRef.current = nextVariables;
179
+ setVariables(nextVariables);
180
+ setIsFetching(true);
181
+ setStatus("loading");
182
+ optionsRef.current?.onLoading?.(true);
183
+ mergedConfig.onQueryLoading?.(true);
184
+ if (optionsRef.current?.staleTime) {
185
+ const cached = IgniterCallerClientCache.get(
186
+ queryKey,
187
+ optionsRef.current.staleTime
188
+ );
189
+ if (cached !== void 0) {
190
+ setResponse({ data: cached, error: null });
191
+ setStatus("success");
192
+ optionsRef.current?.onSuccess?.(cached);
193
+ mergedConfig.onQuerySuccess?.(cached);
194
+ optionsRef.current?.onLoading?.(false);
195
+ mergedConfig.onQueryLoading?.(false);
196
+ setIsFetching(false);
197
+ return {
198
+ data: cached,
199
+ error: void 0
200
+ };
201
+ }
202
+ }
203
+ try {
204
+ const result = await buildRequest(method, path, mergedInput).execute();
205
+ optionsRef.current?.onRequest?.(result);
206
+ if (result.error) {
207
+ setResponse({ data: null, error: result.error });
208
+ setStatus("error");
209
+ optionsRef.current?.onError?.(result.error);
210
+ mergedConfig.onQueryError?.(result.error);
211
+ onError?.(result.error);
212
+ } else {
213
+ const data = result.data ?? null;
214
+ setResponse({ data, error: null });
215
+ setStatus("success");
216
+ optionsRef.current?.onSuccess?.(data);
217
+ mergedConfig.onQuerySuccess?.(data);
218
+ if (optionsRef.current?.staleTime && data !== null) {
219
+ IgniterCallerClientCache.set(queryKey, data);
220
+ }
221
+ }
222
+ optionsRef.current?.onSettled?.(
223
+ result.data ?? null,
224
+ result.error ?? null
225
+ );
226
+ mergedConfig.onQuerySettled?.(
227
+ result.data ?? null,
228
+ result.error ?? null
229
+ );
230
+ return result;
231
+ } catch (error) {
232
+ const typedError = error;
233
+ setResponse({ data: null, error: typedError });
234
+ setStatus("error");
235
+ optionsRef.current?.onError?.(typedError);
236
+ mergedConfig.onQueryError?.(typedError);
237
+ onError?.(typedError);
238
+ optionsRef.current?.onSettled?.(null, typedError);
239
+ mergedConfig.onQuerySettled?.(null, typedError);
240
+ } finally {
241
+ setIsFetching(false);
242
+ optionsRef.current?.onLoading?.(false);
243
+ mergedConfig.onQueryLoading?.(false);
244
+ }
245
+ },
246
+ [callerKey, method, path, buildRequest, getMergedConfig, onError]
247
+ );
248
+ const refetch = useCallback(() => {
249
+ void execute(lastVariablesRef.current);
250
+ }, [execute]);
251
+ const invalidateQuery = useCallback(
252
+ (data) => {
253
+ const prefix = buildQueryPrefix({ callerKey, method, path });
254
+ invalidate(prefix, data);
255
+ },
256
+ [callerKey, method, path, invalidate]
257
+ );
258
+ useEffect(() => {
259
+ register(stableKey, refetch);
260
+ return () => unregister(stableKey, refetch);
261
+ }, [register, unregister, refetch, stableKey]);
262
+ useEffect(() => {
263
+ if (optionsRef.current?.enabled === false) return;
264
+ if (optionsRef.current?.refetchOnMount === false) return;
265
+ void execute(lastVariablesRef.current);
266
+ }, [stableKey, execute]);
267
+ useEffect(() => {
268
+ const interval = optionsRef.current?.refetchInterval;
269
+ if (!interval) return;
270
+ const handler = setInterval(() => {
271
+ if (optionsRef.current?.refetchIntervalInBackground === false && typeof document !== "undefined" && document.hidden) {
272
+ return;
273
+ }
274
+ void execute(lastVariablesRef.current);
275
+ }, interval);
276
+ return () => clearInterval(handler);
277
+ }, [execute]);
278
+ useEffect(() => {
279
+ if (optionsRef.current?.refetchOnWindowFocus === false) return;
280
+ if (typeof window === "undefined") return;
281
+ const handler = () => {
282
+ void execute(lastVariablesRef.current);
283
+ };
284
+ window.addEventListener("focus", handler);
285
+ return () => window.removeEventListener("focus", handler);
286
+ }, [execute]);
287
+ return {
288
+ data: response.data,
289
+ error: response.error,
290
+ variables,
291
+ isLoading: status === "loading",
292
+ isFetching,
293
+ isSuccess: status === "success",
294
+ isError: status === "error",
295
+ status,
296
+ refetch,
297
+ invalidate: invalidateQuery,
298
+ execute
299
+ };
300
+ };
301
+ }
302
+ function createUseMutate(params) {
303
+ const {
304
+ callerKey,
305
+ method,
306
+ path,
307
+ buildRequest,
308
+ getMergedConfig,
309
+ onError
310
+ } = params;
311
+ return (options) => {
312
+ const [status, setStatus] = useState(
313
+ "success"
314
+ );
315
+ const [response, setResponse] = useState({ data: null, error: null });
316
+ const [variables, setVariables] = useState(
317
+ void 0
318
+ );
319
+ const optionsRef = useRef(options);
320
+ optionsRef.current = options;
321
+ const lastVariablesRef = useRef(void 0);
322
+ const mutate = useCallback(
323
+ async (nextVariables) => {
324
+ const mergedConfig = getMergedConfig();
325
+ const mergedInput = mergeMutateInput(
326
+ mergedConfig,
327
+ optionsRef.current,
328
+ nextVariables
329
+ );
330
+ lastVariablesRef.current = nextVariables;
331
+ setVariables(nextVariables);
332
+ setStatus("loading");
333
+ optionsRef.current?.onLoading?.(true);
334
+ mergedConfig.onMutationLoading?.(true);
335
+ try {
336
+ const result = await buildRequest(method, path, mergedInput).execute();
337
+ optionsRef.current?.onRequest?.(result);
338
+ if (result.error) {
339
+ setResponse({ data: null, error: result.error });
340
+ setStatus("error");
341
+ optionsRef.current?.onError?.(result.error);
342
+ mergedConfig.onMutationError?.(result.error);
343
+ onError?.(result.error);
344
+ } else {
345
+ const data = result.data ?? null;
346
+ setResponse({ data, error: null });
347
+ setStatus("success");
348
+ optionsRef.current?.onSuccess?.(data);
349
+ mergedConfig.onMutationSuccess?.(data);
350
+ }
351
+ optionsRef.current?.onSettled?.(
352
+ result.data ?? null,
353
+ result.error ?? null
354
+ );
355
+ mergedConfig.onMutationSettled?.(
356
+ result.data ?? null,
357
+ result.error ?? null
358
+ );
359
+ return result;
360
+ } catch (error) {
361
+ const typedError = error;
362
+ setResponse({ data: null, error: typedError });
363
+ setStatus("error");
364
+ optionsRef.current?.onError?.(typedError);
365
+ mergedConfig.onMutationError?.(typedError);
366
+ onError?.(typedError);
367
+ optionsRef.current?.onSettled?.(null, typedError);
368
+ mergedConfig.onMutationSettled?.(null, typedError);
369
+ } finally {
370
+ optionsRef.current?.onLoading?.(false);
371
+ mergedConfig.onMutationLoading?.(false);
372
+ }
373
+ },
374
+ [method, path, buildRequest, getMergedConfig, onError]
375
+ );
376
+ const retry = useCallback(() => {
377
+ void mutate(lastVariablesRef.current);
378
+ }, [mutate]);
379
+ return {
380
+ data: response.data,
381
+ error: response.error,
382
+ variables,
383
+ isLoading: status === "loading",
384
+ isSuccess: status === "success",
385
+ isError: status === "error",
386
+ status,
387
+ mutate,
388
+ retry
389
+ };
390
+ };
391
+ }
392
+
393
+ // src/client/builders/client.builder.ts
394
+ function applyRequestInput(builder, input) {
395
+ if (input.query && Object.keys(input.query).length > 0) {
396
+ builder.params(input.query);
397
+ }
398
+ if (input.headers && Object.keys(input.headers).length > 0) {
399
+ builder.headers(input.headers);
400
+ }
401
+ if (input.body !== void 0) {
402
+ builder.body(input.body);
403
+ }
404
+ }
405
+ function createIgniterCallerClient(context, callerKey) {
406
+ const caller = context.callers[callerKey];
407
+ const getMergedConfig = () => context.getMergedConfig(callerKey);
408
+ const buildRequest = (method, path, input) => {
409
+ if (!path) {
410
+ throw new Error(
411
+ "IgniterCallerClient: path is required when using hooks with Caller client."
412
+ );
413
+ }
414
+ const methodKey = method.toLowerCase();
415
+ const builder = caller[methodKey](path);
416
+ applyRequestInput(builder, input);
417
+ return builder;
418
+ };
419
+ const invalidate = (pathOrPaths, data) => {
420
+ const paths = Array.isArray(pathOrPaths) ? pathOrPaths : [pathOrPaths];
421
+ const prefixes = paths.map(
422
+ (path) => buildQueryPrefix({
423
+ callerKey: String(callerKey),
424
+ method: "GET",
425
+ path
426
+ })
427
+ );
428
+ context.invalidate(prefixes, data);
429
+ };
430
+ const config = {
431
+ set: (key, value) => context.setConfig(callerKey, key, value),
432
+ get: (key) => context.getConfig(callerKey, key),
433
+ reset: () => context.setConfigBatch(callerKey, {})
434
+ };
435
+ const get = (path) => {
436
+ const builder = caller.get(path);
437
+ const useQuery = createUseQuery({
438
+ callerKey: String(callerKey),
439
+ method: "GET",
440
+ path: path || "",
441
+ buildRequest,
442
+ register: context.register,
443
+ unregister: context.unregister,
444
+ invalidate: context.invalidate,
445
+ getMergedConfig,
446
+ onError: context.onError
447
+ });
448
+ builder.useQuery = useQuery;
449
+ return builder;
450
+ };
451
+ const head = (path) => {
452
+ const builder = caller.head(path);
453
+ const useQuery = createUseQuery({
454
+ callerKey: String(callerKey),
455
+ method: "HEAD",
456
+ path: path || "",
457
+ buildRequest,
458
+ register: context.register,
459
+ unregister: context.unregister,
460
+ invalidate: context.invalidate,
461
+ getMergedConfig,
462
+ onError: context.onError
463
+ });
464
+ builder.useQuery = useQuery;
465
+ return builder;
466
+ };
467
+ const post = (path) => {
468
+ const builder = caller.post(path);
469
+ const useMutate = createUseMutate({
470
+ callerKey: String(callerKey),
471
+ method: "POST",
472
+ path: path || "",
473
+ buildRequest,
474
+ getMergedConfig,
475
+ onError: context.onError
476
+ });
477
+ builder.useMutate = useMutate;
478
+ return builder;
479
+ };
480
+ const put = (path) => {
481
+ const builder = caller.put(path);
482
+ const useMutate = createUseMutate({
483
+ callerKey: String(callerKey),
484
+ method: "PUT",
485
+ path: path || "",
486
+ buildRequest,
487
+ getMergedConfig,
488
+ onError: context.onError
489
+ });
490
+ builder.useMutate = useMutate;
491
+ return builder;
492
+ };
493
+ const patch = (path) => {
494
+ const builder = caller.patch(path);
495
+ const useMutate = createUseMutate({
496
+ callerKey: String(callerKey),
497
+ method: "PATCH",
498
+ path: path || "",
499
+ buildRequest,
500
+ getMergedConfig,
501
+ onError: context.onError
502
+ });
503
+ builder.useMutate = useMutate;
504
+ return builder;
505
+ };
506
+ const del = (path) => {
507
+ const builder = caller.delete(path);
508
+ const useMutate = createUseMutate({
509
+ callerKey: String(callerKey),
510
+ method: "DELETE",
511
+ path: path || "",
512
+ buildRequest,
513
+ getMergedConfig,
514
+ onError: context.onError
515
+ });
516
+ builder.useMutate = useMutate;
517
+ return builder;
518
+ };
519
+ return {
520
+ raw: caller,
521
+ config,
522
+ invalidate,
523
+ get,
524
+ post,
525
+ put,
526
+ patch,
527
+ delete: del,
528
+ head
529
+ };
530
+ }
531
+ var IgniterCallerClientContext = createContext(void 0);
532
+ function mergeConfig(globalConfig, localConfig) {
533
+ return {
534
+ ...globalConfig,
535
+ ...localConfig,
536
+ headers: {
537
+ ...globalConfig.headers || {},
538
+ ...localConfig?.headers || {}
539
+ },
540
+ cookies: {
541
+ ...globalConfig.cookies || {},
542
+ ...localConfig?.cookies || {}
543
+ },
544
+ query: {
545
+ ...globalConfig.query || {},
546
+ ...localConfig?.query || {}
547
+ }
548
+ };
549
+ }
550
+ function IgniterCallerProvider(props) {
551
+ const { callers, configs, hooks, onError, children } = props;
552
+ const [configState, setConfigState] = useState(configs || {});
553
+ const [listeners] = useState(
554
+ () => /* @__PURE__ */ new Map()
555
+ );
556
+ const globalConfig = useMemo(
557
+ () => hooks || {},
558
+ [hooks]
559
+ );
560
+ const register = useCallback(
561
+ (key, refetch) => {
562
+ const current = listeners.get(key) || /* @__PURE__ */ new Set();
563
+ current.add(refetch);
564
+ listeners.set(key, current);
565
+ },
566
+ [listeners]
567
+ );
568
+ const unregister = useCallback(
569
+ (key, refetch) => {
570
+ const current = listeners.get(key);
571
+ if (!current) return;
572
+ current.delete(refetch);
573
+ if (current.size === 0) {
574
+ listeners.delete(key);
575
+ }
576
+ },
577
+ [listeners]
578
+ );
579
+ const invalidate = useCallback(
580
+ (keys, data) => {
581
+ const keysArray = Array.isArray(keys) ? keys : [keys];
582
+ keysArray.forEach((prefix) => {
583
+ if (data !== void 0) {
584
+ IgniterCallerClientCache.replacePrefix(prefix, data);
585
+ } else {
586
+ IgniterCallerClientCache.clearPrefix(prefix);
587
+ }
588
+ listeners.forEach((refetchFns, registeredKey) => {
589
+ if (registeredKey.startsWith(prefix)) {
590
+ refetchFns.forEach((refetch) => refetch(true));
591
+ }
592
+ });
593
+ });
594
+ },
595
+ [listeners]
596
+ );
597
+ const setConfig = useCallback(
598
+ (callerKey, key, value2) => {
599
+ setConfigState((prev) => ({
600
+ ...prev,
601
+ [callerKey]: {
602
+ ...prev[callerKey] || {},
603
+ [key]: value2
604
+ }
605
+ }));
606
+ },
607
+ []
608
+ );
609
+ const setConfigBatch = useCallback(
610
+ (callerKey, config) => {
611
+ setConfigState((prev) => ({
612
+ ...prev,
613
+ [callerKey]: {
614
+ ...prev[callerKey] || {},
615
+ ...config
616
+ }
617
+ }));
618
+ },
619
+ []
620
+ );
621
+ const getMergedConfig = useCallback(
622
+ (callerKey) => mergeConfig(globalConfig, configState[callerKey]),
623
+ [globalConfig, configState]
624
+ );
625
+ const getConfig = useCallback(
626
+ (callerKey, key) => getMergedConfig(callerKey)[key],
627
+ [getMergedConfig]
628
+ );
629
+ const value = useMemo(
630
+ () => ({
631
+ callers,
632
+ configs: configState,
633
+ globalConfig,
634
+ setConfig,
635
+ setConfigBatch,
636
+ getConfig,
637
+ getMergedConfig,
638
+ register,
639
+ unregister,
640
+ invalidate,
641
+ listeners,
642
+ onError
643
+ }),
644
+ [
645
+ callers,
646
+ configState,
647
+ globalConfig,
648
+ getConfig,
649
+ getMergedConfig,
650
+ invalidate,
651
+ listeners,
652
+ onError,
653
+ register,
654
+ setConfig,
655
+ setConfigBatch,
656
+ unregister
657
+ ]
658
+ );
659
+ return /* @__PURE__ */ jsx(IgniterCallerClientContext.Provider, { value, children });
660
+ }
661
+ function useIgniterCallerContext() {
662
+ const context = useContext(
663
+ IgniterCallerClientContext
664
+ );
665
+ if (!context) {
666
+ throw new Error(
667
+ "IgniterCallerProvider is missing. Wrap your app with <IgniterCallerProvider>."
668
+ );
669
+ }
670
+ return context;
671
+ }
672
+ function useIgniterCaller() {
673
+ const context = useIgniterCallerContext();
674
+ return (callerKey) => createIgniterCallerClient(context, callerKey);
675
+ }
676
+
677
+ export { IgniterCallerClientCache, IgniterCallerProvider, buildCookieHeader, buildQueryKey, buildQueryPrefix, createIgniterCallerClient, useIgniterCaller, useIgniterCallerContext };
678
+ //# sourceMappingURL=index.mjs.map
679
+ //# sourceMappingURL=index.mjs.map