@betterinternship/components 1.5.19 → 1.5.20

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.
@@ -1,4 +1,3 @@
1
- import { UseQueryResult } from '@tanstack/react-query';
2
1
  interface ClientProcess<T> {
3
2
  dto?: ProcessCallbackDto;
4
3
  metadata?: ClientProcessMetadata;
@@ -64,6 +63,6 @@ export declare function useClientProcess<T>({ caller, filterKey, onLoading, onSu
64
63
  getAllPending: () => ClientProcess<unknown>[];
65
64
  getAllHandled: () => ClientProcess<unknown>[];
66
65
  getAllFailed: () => ClientProcess<unknown>[];
67
- cp: UseQueryResult[];
66
+ refreshCache: () => Promise<void>;
68
67
  };
69
68
  export {};
@@ -79,11 +79,11 @@ const ClientProcessesProvider = ({ children }) => {
79
79
  const clientProcesses = useQueries({
80
80
  queries: Object.entries(clientProcessDtos).map(([processId, { dto, metadata, options }]) => ({
81
81
  staleTime: Infinity,
82
- gcTime: 0,
82
+ gcTime: Infinity,
83
83
  refetchOnWindowFocus: true,
84
84
  refetchOnMount: true,
85
85
  refetchOnReconnect: true,
86
- queryKey: ['.__process', processId],
86
+ queryKey: ['.__process', metadata.filterKey, processId],
87
87
  queryFn: async () => {
88
88
  if (!processId)
89
89
  throw new Error('No processId');
@@ -94,15 +94,6 @@ const ClientProcessesProvider = ({ children }) => {
94
94
  // Log and call runner
95
95
  console.warn(`Handled ${process.name} (${process.id})!`);
96
96
  options?.onSuccess?.(process.id, process.name, result);
97
- // Invalidate query only if initially pending
98
- if (queryClient
99
- .getQueryCache()
100
- .findAll({ queryKey: ['.__process', processId] })
101
- .every((q) => q.state.data.pending)) {
102
- await queryClient.invalidateQueries({
103
- queryKey: ['.__process', processId],
104
- });
105
- }
106
97
  return {
107
98
  dto,
108
99
  metadata,
@@ -120,15 +111,6 @@ const ClientProcessesProvider = ({ children }) => {
120
111
  }
121
112
  else if (process?.status === 'failed') {
122
113
  console.error(`Failed ${process.name} (${process.id}) - ${process.error}.`);
123
- // Invalidate query only if initially pending
124
- if (queryClient
125
- .getQueryCache()
126
- .findAll({ queryKey: ['.__process', processId] })
127
- .every((q) => q.state.data.pending)) {
128
- await queryClient.invalidateQueries({
129
- queryKey: ['.__process', processId],
130
- });
131
- }
132
114
  return {
133
115
  dto,
134
116
  metadata,
@@ -144,8 +126,12 @@ const ClientProcessesProvider = ({ children }) => {
144
126
  // Returns the next interval length to use to wait for fetch
145
127
  refetchInterval: (query) => {
146
128
  const data = query.state.data;
147
- if (data?.success !== undefined)
129
+ if (data?.success !== undefined) {
130
+ void queryClient.invalidateQueries({
131
+ queryKey: ['.__process', metadata.filterKey, processId],
132
+ });
148
133
  return false;
134
+ }
149
135
  // If not defined, init to 0
150
136
  if (!clientProcessAttempts.current[processId])
151
137
  clientProcessAttempts.current[processId] = 0;
@@ -189,11 +175,20 @@ const ClientProcessesProvider = ({ children }) => {
189
175
  .map((clientProcess) => clientProcess.data)
190
176
  .filter((clientProcessData) => clientProcessData?.metadata.filterKey === filterKey);
191
177
  };
178
+ /**
179
+ * Refreshes the cache and invalidates all related processes.
180
+ */
181
+ const refreshCache = async (filter) => {
182
+ await queryClient.invalidateQueries({
183
+ queryKey: ['.__process', filter],
184
+ exact: false,
185
+ });
186
+ };
192
187
  return (jsxRuntimeExports.jsx(clientProcessesContext.Provider, { value: {
193
188
  registerProcess,
194
189
  getProcess,
195
190
  filterProcesses,
196
- cp: clientProcesses,
191
+ refreshCache,
197
192
  }, children: children }));
198
193
  };
199
194
  /**
@@ -240,7 +235,7 @@ function useClientProcess({ caller, filterKey, onLoading, onSuccess, onFailure,
240
235
  getAllFailed: () => clientProcessesApi
241
236
  .filterProcesses(filterKey)
242
237
  .filter((process) => !process.success),
243
- cp: clientProcessesApi.cp,
238
+ refreshCache: () => clientProcessesApi.refreshCache(filterKey),
244
239
  };
245
240
  }
246
241
 
@@ -1 +1 @@
1
- {"version":3,"file":"useClientProcess.js","sources":["../../src/hooks/useClientProcess.tsx"],"sourcesContent":["'use client';\r\n\r\nimport { useState, useRef, createContext, useContext, useEffect } from 'react';\r\nimport {\r\n Query,\r\n useQueries,\r\n useQueryClient,\r\n UseQueryResult,\r\n} from '@tanstack/react-query';\r\nimport { config } from '../config';\r\n\r\ntype ProcessStatus = 'pending' | 'handled' | 'failed';\r\n\r\ninterface Process<T> {\r\n id: string;\r\n name: string;\r\n status: ProcessStatus;\r\n result?: T;\r\n error?: string;\r\n timestamp: string;\r\n}\r\n\r\ninterface ClientProcess<T> {\r\n dto?: ProcessCallbackDto;\r\n metadata?: ClientProcessMetadata;\r\n options?: ClientProcessOptions;\r\n result?: T;\r\n success?: boolean;\r\n pending?: boolean;\r\n error?: string;\r\n}\r\n\r\nexport interface ProcessCallbackDto {\r\n processId: string;\r\n processName: string;\r\n processCallbackUrl: string;\r\n}\r\n\r\nexport interface ClientProcessOptions {\r\n onLoading?: (processId: string, processName: string) => void;\r\n onSuccess?: <R>(processId: string, processName: string, result: R) => void;\r\n onFailure?: (processId: string, processName: string, error?: string) => void;\r\n}\r\n\r\nexport interface ClientProcessMetadata {\r\n filterKey: string;\r\n metadata?: Record<string, string>;\r\n}\r\n\r\nexport interface ProcessResponse {\r\n processId: string;\r\n processName: string;\r\n processCallbackUrl: string;\r\n success: boolean;\r\n message?: string;\r\n}\r\n\r\n/**\r\n * The orchestrator service to get the process from.\r\n *\r\n * @param processId\r\n * @returns\r\n */\r\nconst processStatusChecker = <T,>(processId: string) => {\r\n if (!processId) return { process: null };\r\n return fetch(`${config.orchestratorApi}/${encodeURIComponent(processId)}`, {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n }).then((response) => response.json()) as unknown as Promise<{\r\n process: Omit<Process<T>, 'result'>;\r\n }>;\r\n};\r\n\r\n/**\r\n * This fetches from the callback endpoint.\r\n *\r\n * @param processId\r\n * @returns\r\n */\r\nconst processCallbackResultFetcher = <T,>(\r\n processId: string,\r\n processName: string,\r\n processCallbackUrl?: string,\r\n) =>\r\n fetch(`${processCallbackUrl}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify({\r\n processId,\r\n processName,\r\n }),\r\n }).then((response) => response.json()) as unknown as Promise<T>;\r\n\r\ninterface ClientProcesses {\r\n registerProcess: (\r\n processId: string,\r\n dto: ProcessCallbackDto,\r\n metadata: ClientProcessMetadata,\r\n options?: ClientProcessOptions,\r\n ) => void;\r\n getProcess: <T>(processId: string) => ClientProcess<T> | null;\r\n filterProcesses: <T>(filter: string) => ClientProcess<T>[];\r\n cp: UseQueryResult[];\r\n}\r\n\r\n/**\r\n * We need a context to keep track of all client processes.\r\n * This helps us manage the state of the toasts as well.\r\n */\r\nconst clientProcessesContext = createContext<ClientProcesses>(\r\n {} as ClientProcesses,\r\n);\r\n\r\n/**\r\n * This should be at the root layout.\r\n *\r\n * @context\r\n */\r\nexport const ClientProcessesProvider = ({ children }: { children: React.ReactNode }) => {\r\n const queryClient = useQueryClient();\r\n const clientProcessAttempts = useRef<Record<string, number>>({});\r\n const [clientProcessDtos, setClientProcessDtos] = useState<\r\n Record<\r\n string,\r\n {\r\n dto: ProcessCallbackDto;\r\n metadata: ClientProcessMetadata;\r\n options?: ClientProcessOptions;\r\n }\r\n >\r\n >({});\r\n\r\n // Populates the state object with cached data on mount\r\n useEffect(() => {\r\n setClientProcessDtos((oldData) => {\r\n if (Object.keys(oldData).length > 0) return oldData;\r\n\r\n const cachedClientProcesses = queryClient\r\n .getQueryCache()\r\n .findAll({ queryKey: ['.__process'], exact: false });\r\n const seededClientProcesses: typeof oldData = {};\r\n\r\n for (const query of cachedClientProcesses) {\r\n const processId = query.queryKey[1];\r\n if (typeof processId !== 'string') continue;\r\n\r\n const data = query.state.data as ClientProcess<any>;\r\n if (!data?.dto || !data?.metadata) continue;\r\n\r\n seededClientProcesses[processId] = {\r\n dto: data.dto,\r\n metadata: data.metadata,\r\n options: data.options,\r\n };\r\n }\r\n\r\n return { ...oldData, ...seededClientProcesses };\r\n });\r\n }, [queryClient]);\r\n\r\n // The query for all the processes, pending and sht\r\n const clientProcesses = useQueries({\r\n queries: Object.entries(clientProcessDtos).map(\r\n ([processId, { dto, metadata, options }]) => ({\r\n staleTime: Infinity,\r\n gcTime: 0,\r\n refetchOnWindowFocus: true,\r\n refetchOnMount: true,\r\n refetchOnReconnect: true,\r\n queryKey: ['.__process', processId],\r\n queryFn: async () => {\r\n if (!processId) throw new Error('No processId');\r\n const { process } = await processStatusChecker(processId);\r\n\r\n // Handled! Return the proper response too\r\n if (process?.status === 'handled') {\r\n const result = await processCallbackResultFetcher(\r\n process.id,\r\n process.name,\r\n dto.processCallbackUrl,\r\n );\r\n\r\n // Log and call runner\r\n console.warn(`Handled ${process.name} (${process.id})!`);\r\n options?.onSuccess?.(process.id, process.name, result);\r\n\r\n // Invalidate query only if initially pending\r\n if (\r\n queryClient\r\n .getQueryCache()\r\n .findAll({ queryKey: ['.__process', processId] })\r\n .every((q) => (q.state.data as ClientProcess<any>).pending)\r\n ) {\r\n await queryClient.invalidateQueries({\r\n queryKey: ['.__process', processId],\r\n });\r\n }\r\n\r\n return {\r\n dto,\r\n metadata,\r\n options,\r\n success: true,\r\n pending: false,\r\n result,\r\n };\r\n }\r\n\r\n // Pending or failed\r\n if (process?.status === 'pending') {\r\n console.log(`Running process ${process.name} (${process.id})...`);\r\n options?.onLoading?.(process.id, process.name);\r\n return { dto, metadata, options, pending: true };\r\n } else if (process?.status === 'failed') {\r\n console.error(\r\n `Failed ${process.name} (${process.id}) - ${process.error}.`,\r\n );\r\n\r\n // Invalidate query only if initially pending\r\n if (\r\n queryClient\r\n .getQueryCache()\r\n .findAll({ queryKey: ['.__process', processId] })\r\n .every((q) => (q.state.data as ClientProcess<any>).pending)\r\n ) {\r\n await queryClient.invalidateQueries({\r\n queryKey: ['.__process', processId],\r\n });\r\n }\r\n\r\n return {\r\n dto,\r\n metadata,\r\n options,\r\n success: false,\r\n pending: false,\r\n error: process.error,\r\n };\r\n }\r\n\r\n // Return null since no response\r\n return { dto, metadata, options, pending: true };\r\n },\r\n\r\n // Returns the next interval length to use to wait for fetch\r\n refetchInterval: <T,>(query: Query) => {\r\n const data = query.state.data as ClientProcess<T>;\r\n if (data?.success !== undefined) return false;\r\n\r\n // If not defined, init to 0\r\n if (!clientProcessAttempts.current[processId])\r\n clientProcessAttempts.current[processId] = 0;\r\n\r\n const interval = Math.min(\r\n 500 * 2 ** clientProcessAttempts.current[processId],\r\n 10000,\r\n );\r\n\r\n clientProcessAttempts.current[processId] += 1;\r\n return interval;\r\n },\r\n }),\r\n ),\r\n });\r\n\r\n /**\r\n * Registers a new process to handle\r\n *\r\n * @param processId\r\n * @param processCallbackDto\r\n * @param options\r\n */\r\n const registerProcess = (\r\n processId: string,\r\n processCallbackDto: ProcessCallbackDto,\r\n metadata: ClientProcessMetadata,\r\n options?: ClientProcessOptions,\r\n ) => {\r\n setClientProcessDtos({\r\n ...clientProcessDtos,\r\n [processId]: { dto: processCallbackDto, metadata, options },\r\n });\r\n };\r\n\r\n /**\r\n * Retrieves a process\r\n *\r\n * @param processId\r\n * @returns\r\n */\r\n const getProcess = <T,>(processId: string) => {\r\n const clientProcess =\r\n clientProcesses.find(\r\n (clientProcess) => clientProcess.data?.dto?.processId === processId,\r\n )?.data ?? null;\r\n\r\n return (clientProcess as ClientProcess<T>) ?? null;\r\n };\r\n\r\n /**\r\n * Filters processes by the provided attribute.\r\n *\r\n * @param filterKey\r\n */\r\n const filterProcesses = <T,>(filterKey: string) => {\r\n return clientProcesses\r\n .filter((clientProcess) => !!clientProcess.data?.dto)\r\n .map((clientProcess) => clientProcess.data)\r\n .filter(\r\n (clientProcessData) =>\r\n clientProcessData?.metadata.filterKey === filterKey,\r\n ) as ClientProcess<T>[];\r\n };\r\n\r\n return (\r\n <clientProcessesContext.Provider\r\n value={{\r\n registerProcess,\r\n getProcess,\r\n filterProcesses,\r\n cp: clientProcesses,\r\n }}\r\n >\r\n {children}\r\n </clientProcessesContext.Provider>\r\n );\r\n};;\r\n\r\n/**\r\n * A simpler version, where u just pass the async function that returns process data.\r\n *\r\n * @param caller\r\n * @returns\r\n */\r\nexport function useClientProcess<T>({\r\n caller,\r\n filterKey,\r\n onLoading,\r\n onSuccess,\r\n onFailure,\r\n}: {\r\n caller?: (inputs: T) => Promise<ProcessResponse>;\r\n filterKey: string;\r\n onLoading?: (processId: string, processName: string) => void;\r\n onSuccess?: <R>(processId: string, processName: string, result: R) => void;\r\n onFailure?: (processId: string, processName: string, error?: string) => void;\r\n}) {\r\n const clientProcessesApi = useContext(clientProcessesContext);\r\n\r\n // Note that the exposed function doesn't return process ids\r\n // Client should be oblivious to those\r\n // Abstraction BABY!\r\n const wrappedCaller = async (\r\n inputs: T,\r\n metadata?: Record<string, string>,\r\n ) => {\r\n const response = await caller?.(inputs);\r\n if (response) {\r\n clientProcessesApi.registerProcess(\r\n response.processId,\r\n response,\r\n { filterKey, metadata },\r\n {\r\n onLoading,\r\n onSuccess,\r\n onFailure,\r\n },\r\n );\r\n\r\n return {\r\n id: response.processId,\r\n success: response.success,\r\n message: response.message,\r\n };\r\n }\r\n\r\n return {\r\n id: null,\r\n success: false,\r\n message: 'No `caller` specified for useClientProcess().',\r\n };\r\n };\r\n\r\n return {\r\n run: wrappedCaller,\r\n get: (id: string) => clientProcessesApi.getProcess(id),\r\n getAll: () => clientProcessesApi.filterProcesses(filterKey),\r\n getAllPending: () =>\r\n clientProcessesApi\r\n .filterProcesses(filterKey)\r\n .filter((process) => process.pending),\r\n getAllHandled: () =>\r\n clientProcessesApi\r\n .filterProcesses(filterKey)\r\n .filter((process) => process.success),\r\n getAllFailed: () =>\r\n clientProcessesApi\r\n .filterProcesses(filterKey)\r\n .filter((process) => !process.success),\r\n cp: clientProcessesApi.cp,\r\n };\r\n}"],"names":[],"mappings":";;;;;;AAyDA;;;;;AAKG;AACH;AACE;AAAgB;AAChB;AACE;AACA;AACE;AACD;AACF;AAGH;AAEA;;;;;AAKG;AACH;AAMI;AACA;AACE;AACD;AACD;;;;AAID;AAcH;;;AAGG;AACH;AAIA;;;;AAIG;;AAED;AACA;;;;AAcE;;AACuC;;AAGlC;AACA;;AAGH;;;;AAIE;;;;;;;;;AAUF;AACF;AACF;;;;AAMM;AACA;AACA;AACA;AACA;AACA;;AAEE;AAAgB;;;AAIhB;AACE;;AAOA;AACA;;AAGA;AAEK;;AAEA;;AAGD;AACD;;;;;;AAOD;AACA;;;;;AAMJ;AACE;AACA;;;AAEK;AACL;;AAKA;AAEK;;AAEA;;AAGD;AACD;;;;;;AAOD;AACA;;;;;;;;AAUN;AACE;AACA;AAAiC;;AAGjC;AACE;AAEF;AAKA;AACA;;AAEH;AAEJ;AAED;;;;;;AAMG;;AAOD;AACE;;AAED;AACH;AAEA;;;;;AAKG;AACH;;;AAOA;AAEA;;;;AAIG;AACH;AACE;AACG;;AAEA;AAIL;AAEA;;;;AAMM;;AAMR;AAEA;;;;;AAKG;AACG;AAaJ;;;;;;;AAWI;;;;AAQG;;;;;;;;AAWH;AACA;AACA;;AAEJ;;AAGE;;;AAGA;;;AAIA;;;AAIA;;;;;AAMJ;;"}
1
+ {"version":3,"file":"useClientProcess.js","sources":["../../src/hooks/useClientProcess.tsx"],"sourcesContent":["'use client';\r\n\r\nimport { useState, useRef, createContext, useContext, useEffect } from 'react';\r\nimport { Query, useQueries, useQueryClient } from '@tanstack/react-query';\r\nimport { config } from '../config';\r\n\r\ntype ProcessStatus = 'pending' | 'handled' | 'failed';\r\n\r\ninterface Process<T> {\r\n id: string;\r\n name: string;\r\n status: ProcessStatus;\r\n result?: T;\r\n error?: string;\r\n timestamp: string;\r\n}\r\n\r\ninterface ClientProcess<T> {\r\n dto?: ProcessCallbackDto;\r\n metadata?: ClientProcessMetadata;\r\n options?: ClientProcessOptions;\r\n result?: T;\r\n success?: boolean;\r\n pending?: boolean;\r\n error?: string;\r\n}\r\n\r\nexport interface ProcessCallbackDto {\r\n processId: string;\r\n processName: string;\r\n processCallbackUrl: string;\r\n}\r\n\r\nexport interface ClientProcessOptions {\r\n onLoading?: (processId: string, processName: string) => void;\r\n onSuccess?: <R>(processId: string, processName: string, result: R) => void;\r\n onFailure?: (processId: string, processName: string, error?: string) => void;\r\n}\r\n\r\nexport interface ClientProcessMetadata {\r\n filterKey: string;\r\n metadata?: Record<string, string>;\r\n}\r\n\r\nexport interface ProcessResponse {\r\n processId: string;\r\n processName: string;\r\n processCallbackUrl: string;\r\n success: boolean;\r\n message?: string;\r\n}\r\n\r\n/**\r\n * The orchestrator service to get the process from.\r\n *\r\n * @param processId\r\n * @returns\r\n */\r\nconst processStatusChecker = <T,>(processId: string) => {\r\n if (!processId) return { process: null };\r\n return fetch(`${config.orchestratorApi}/${encodeURIComponent(processId)}`, {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n }).then((response) => response.json()) as unknown as Promise<{\r\n process: Omit<Process<T>, 'result'>;\r\n }>;\r\n};\r\n\r\n/**\r\n * This fetches from the callback endpoint.\r\n *\r\n * @param processId\r\n * @returns\r\n */\r\nconst processCallbackResultFetcher = <T,>(\r\n processId: string,\r\n processName: string,\r\n processCallbackUrl?: string,\r\n) =>\r\n fetch(`${processCallbackUrl}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify({\r\n processId,\r\n processName,\r\n }),\r\n }).then((response) => response.json()) as unknown as Promise<T>;\r\n\r\ninterface ClientProcesses {\r\n registerProcess: (\r\n processId: string,\r\n dto: ProcessCallbackDto,\r\n metadata: ClientProcessMetadata,\r\n options?: ClientProcessOptions,\r\n ) => void;\r\n getProcess: <T>(processId: string) => ClientProcess<T> | null;\r\n filterProcesses: <T>(filter: string) => ClientProcess<T>[];\r\n refreshCache: (filter: string) => Promise<void>;\r\n}\r\n\r\n/**\r\n * We need a context to keep track of all client processes.\r\n * This helps us manage the state of the toasts as well.\r\n */\r\nconst clientProcessesContext = createContext<ClientProcesses>(\r\n {} as ClientProcesses,\r\n);\r\n\r\n/**\r\n * This should be at the root layout.\r\n *\r\n * @context\r\n */\r\nexport const ClientProcessesProvider = ({ children }: { children: React.ReactNode }) => {\r\n const queryClient = useQueryClient();\r\n const clientProcessAttempts = useRef<Record<string, number>>({});\r\n const [clientProcessDtos, setClientProcessDtos] = useState<\r\n Record<\r\n string,\r\n {\r\n dto: ProcessCallbackDto;\r\n metadata: ClientProcessMetadata;\r\n options?: ClientProcessOptions;\r\n }\r\n >\r\n >({});\r\n\r\n // Populates the state object with cached data on mount\r\n useEffect(() => {\r\n setClientProcessDtos((oldData) => {\r\n if (Object.keys(oldData).length > 0) return oldData;\r\n\r\n const cachedClientProcesses = queryClient\r\n .getQueryCache()\r\n .findAll({ queryKey: ['.__process'], exact: false });\r\n const seededClientProcesses: typeof oldData = {};\r\n\r\n for (const query of cachedClientProcesses) {\r\n const processId = query.queryKey[1];\r\n if (typeof processId !== 'string') continue;\r\n\r\n const data = query.state.data as ClientProcess<any>;\r\n if (!data?.dto || !data?.metadata) continue;\r\n\r\n seededClientProcesses[processId] = {\r\n dto: data.dto,\r\n metadata: data.metadata,\r\n options: data.options,\r\n };\r\n }\r\n\r\n return { ...oldData, ...seededClientProcesses };\r\n });\r\n }, [queryClient]);\r\n\r\n // The query for all the processes, pending and sht\r\n const clientProcesses = useQueries({\r\n queries: Object.entries(clientProcessDtos).map(\r\n ([processId, { dto, metadata, options }]) => ({\r\n staleTime: Infinity,\r\n gcTime: Infinity,\r\n refetchOnWindowFocus: true,\r\n refetchOnMount: true,\r\n refetchOnReconnect: true,\r\n queryKey: ['.__process', metadata.filterKey, processId],\r\n queryFn: async () => {\r\n if (!processId) throw new Error('No processId');\r\n const { process } = await processStatusChecker(processId);\r\n\r\n // Handled! Return the proper response too\r\n if (process?.status === 'handled') {\r\n const result = await processCallbackResultFetcher(\r\n process.id,\r\n process.name,\r\n dto.processCallbackUrl,\r\n );\r\n\r\n // Log and call runner\r\n console.warn(`Handled ${process.name} (${process.id})!`);\r\n options?.onSuccess?.(process.id, process.name, result);\r\n\r\n return {\r\n dto,\r\n metadata,\r\n options,\r\n success: true,\r\n pending: false,\r\n result,\r\n };\r\n }\r\n\r\n // Pending or failed\r\n if (process?.status === 'pending') {\r\n console.log(`Running process ${process.name} (${process.id})...`);\r\n options?.onLoading?.(process.id, process.name);\r\n return { dto, metadata, options, pending: true };\r\n } else if (process?.status === 'failed') {\r\n console.error(\r\n `Failed ${process.name} (${process.id}) - ${process.error}.`,\r\n );\r\n\r\n return {\r\n dto,\r\n metadata,\r\n options,\r\n success: false,\r\n pending: false,\r\n error: process.error,\r\n };\r\n }\r\n\r\n // Return null since no response\r\n return { dto, metadata, options, pending: true };\r\n },\r\n\r\n // Returns the next interval length to use to wait for fetch\r\n refetchInterval: <T,>(query: Query) => {\r\n const data = query.state.data as ClientProcess<T>;\r\n if (data?.success !== undefined) {\r\n void queryClient.invalidateQueries({\r\n queryKey: ['.__process', metadata.filterKey, processId],\r\n });\r\n return false;\r\n }\r\n\r\n // If not defined, init to 0\r\n if (!clientProcessAttempts.current[processId])\r\n clientProcessAttempts.current[processId] = 0;\r\n\r\n const interval = Math.min(\r\n 500 * 2 ** clientProcessAttempts.current[processId],\r\n 10000,\r\n );\r\n\r\n clientProcessAttempts.current[processId] += 1;\r\n return interval;\r\n },\r\n }),\r\n ),\r\n });\r\n\r\n /**\r\n * Registers a new process to handle\r\n *\r\n * @param processId\r\n * @param processCallbackDto\r\n * @param options\r\n */\r\n const registerProcess = (\r\n processId: string,\r\n processCallbackDto: ProcessCallbackDto,\r\n metadata: ClientProcessMetadata,\r\n options?: ClientProcessOptions,\r\n ) => {\r\n setClientProcessDtos({\r\n ...clientProcessDtos,\r\n [processId]: { dto: processCallbackDto, metadata, options },\r\n });\r\n };\r\n\r\n /**\r\n * Retrieves a process\r\n *\r\n * @param processId\r\n * @returns\r\n */\r\n const getProcess = <T,>(processId: string) => {\r\n const clientProcess =\r\n clientProcesses.find(\r\n (clientProcess) => clientProcess.data?.dto?.processId === processId,\r\n )?.data ?? null;\r\n\r\n return (clientProcess as ClientProcess<T>) ?? null;\r\n };\r\n\r\n /**\r\n * Filters processes by the provided attribute.\r\n *\r\n * @param filterKey\r\n */\r\n const filterProcesses = <T,>(filterKey: string) => {\r\n return clientProcesses\r\n .filter((clientProcess) => !!clientProcess.data?.dto)\r\n .map((clientProcess) => clientProcess.data)\r\n .filter(\r\n (clientProcessData) =>\r\n clientProcessData?.metadata.filterKey === filterKey,\r\n ) as ClientProcess<T>[];\r\n };\r\n\r\n /**\r\n * Refreshes the cache and invalidates all related processes.\r\n */\r\n const refreshCache = async (filter: string) => {\r\n await queryClient.invalidateQueries({\r\n queryKey: ['.__process', filter],\r\n exact: false,\r\n });\r\n };\r\n\r\n return (\r\n <clientProcessesContext.Provider\r\n value={{\r\n registerProcess,\r\n getProcess,\r\n filterProcesses,\r\n refreshCache,\r\n }}\r\n >\r\n {children}\r\n </clientProcessesContext.Provider>\r\n );\r\n};;;\r\n\r\n/**\r\n * A simpler version, where u just pass the async function that returns process data.\r\n *\r\n * @param caller\r\n * @returns\r\n */\r\nexport function useClientProcess<T>({\r\n caller,\r\n filterKey,\r\n onLoading,\r\n onSuccess,\r\n onFailure,\r\n}: {\r\n caller?: (inputs: T) => Promise<ProcessResponse>;\r\n filterKey: string;\r\n onLoading?: (processId: string, processName: string) => void;\r\n onSuccess?: <R>(processId: string, processName: string, result: R) => void;\r\n onFailure?: (processId: string, processName: string, error?: string) => void;\r\n}) {\r\n const clientProcessesApi = useContext(clientProcessesContext);\r\n\r\n // Note that the exposed function doesn't return process ids\r\n // Client should be oblivious to those\r\n // Abstraction BABY!\r\n const wrappedCaller = async (\r\n inputs: T,\r\n metadata?: Record<string, string>,\r\n ) => {\r\n const response = await caller?.(inputs);\r\n if (response) {\r\n clientProcessesApi.registerProcess(\r\n response.processId,\r\n response,\r\n { filterKey, metadata },\r\n {\r\n onLoading,\r\n onSuccess,\r\n onFailure,\r\n },\r\n );\r\n\r\n return {\r\n id: response.processId,\r\n success: response.success,\r\n message: response.message,\r\n };\r\n }\r\n\r\n return {\r\n id: null,\r\n success: false,\r\n message: 'No `caller` specified for useClientProcess().',\r\n };\r\n };\r\n\r\n return {\r\n run: wrappedCaller,\r\n get: (id: string) => clientProcessesApi.getProcess(id),\r\n getAll: () => clientProcessesApi.filterProcesses(filterKey),\r\n getAllPending: () =>\r\n clientProcessesApi\r\n .filterProcesses(filterKey)\r\n .filter((process) => process.pending),\r\n getAllHandled: () =>\r\n clientProcessesApi\r\n .filterProcesses(filterKey)\r\n .filter((process) => process.success),\r\n getAllFailed: () =>\r\n clientProcessesApi\r\n .filterProcesses(filterKey)\r\n .filter((process) => !process.success),\r\n refreshCache: () => clientProcessesApi.refreshCache(filterKey),\r\n };\r\n}"],"names":[],"mappings":";;;;;;AAoDA;;;;;AAKG;AACH;AACE;AAAgB;AAChB;AACE;AACA;AACE;AACD;AACF;AAGH;AAEA;;;;;AAKG;AACH;AAMI;AACA;AACE;AACD;AACD;;;;AAID;AAcH;;;AAGG;AACH;AAIA;;;;AAIG;;AAED;AACA;;;;AAcE;;AACuC;;AAGlC;AACA;;AAGH;;;;AAIE;;;;;;;;;AAUF;AACF;AACF;;;;AAMM;AACA;AACA;AACA;AACA;;;AAGE;AAAgB;;;AAIhB;AACE;;AAOA;AACA;;;;;AAME;AACA;;;;;AAMJ;AACE;AACA;;;AAEK;AACL;;;;;AAQE;AACA;;;;;;;;AAUN;AACE;AACA;;;AAGG;AACD;;;AAIF;AACE;AAEF;AAKA;AACA;;AAEH;AAEJ;AAED;;;;;;AAMG;;AAOD;AACE;;AAED;AACH;AAEA;;;;;AAKG;AACH;;;AAOA;AAEA;;;;AAIG;AACH;AACE;AACG;;AAEA;AAIL;AAEA;;AAEG;AACH;;AAEI;AACA;AACD;AACH;AAEA;;;;;;AAYF;AAEA;;;;;AAKG;AACG;AAaJ;;;;;;;AAWI;;;;AAQG;;;;;;;;AAWH;AACA;AACA;;AAEJ;;AAGE;;;AAGA;;;AAIA;;;AAIA;;;;;AAMJ;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@betterinternship/components",
3
- "version": "1.5.19",
3
+ "version": "1.5.20",
4
4
  "main": "dist/index.esm.js",
5
5
  "module": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",