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