@betterinternship/components 1.5.15 → 1.5.17

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,9 +1,9 @@
1
1
  import { UseQueryResult } from '@tanstack/react-query';
2
2
  interface ClientProcess<T> {
3
- id: string;
4
- name: string;
5
- result?: T;
3
+ dto?: ProcessCallbackDto;
6
4
  metadata?: ClientProcessMetadata;
5
+ options?: ClientProcessOptions;
6
+ result?: T;
7
7
  success?: boolean;
8
8
  pending?: boolean;
9
9
  error?: string;
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { j as jsxRuntimeExports } from '../_virtual/jsx-runtime.js';
3
- import { createContext, useRef, useState, useContext } from 'react';
4
- import { useQueries } from '@tanstack/react-query';
3
+ import { createContext, useRef, useState, useEffect, useContext } from 'react';
4
+ import { useQueryClient, useQueries } from '@tanstack/react-query';
5
5
  import { config } from '../config.js';
6
6
 
7
7
  /**
@@ -47,12 +47,42 @@ const clientProcessesContext = createContext({});
47
47
  * @context
48
48
  */
49
49
  const ClientProcessesProvider = ({ children }) => {
50
+ const queryClient = useQueryClient();
50
51
  const clientProcessAttempts = useRef({});
51
52
  const [clientProcessDtos, setClientProcessDtos] = useState({});
53
+ // Populates the state object with cached data on mount
54
+ useEffect(() => {
55
+ setClientProcessDtos((oldData) => {
56
+ if (Object.keys(oldData).length > 0)
57
+ return oldData;
58
+ const cachedClientProcesses = queryClient
59
+ .getQueryCache()
60
+ .findAll({ queryKey: ['.__process'], exact: false });
61
+ const seededClientProcesses = {};
62
+ for (const query of cachedClientProcesses) {
63
+ const processId = query.queryKey[1];
64
+ if (typeof processId !== 'string')
65
+ continue;
66
+ const data = query.state.data;
67
+ if (!data?.dto || !data?.metadata)
68
+ continue;
69
+ seededClientProcesses[processId] = {
70
+ dto: data.dto,
71
+ metadata: data.metadata,
72
+ options: data.options,
73
+ };
74
+ }
75
+ return { ...oldData, ...seededClientProcesses };
76
+ });
77
+ }, [queryClient]);
78
+ // The query for all the processes, pending and sht
52
79
  const clientProcesses = useQueries({
53
- queries: Object.entries(clientProcessDtos).map(([processId, { dto, options }]) => ({
80
+ queries: Object.entries(clientProcessDtos).map(([processId, { dto, metadata, options }]) => ({
54
81
  staleTime: Infinity,
55
- gcTime: Infinity,
82
+ gcTime: 0,
83
+ refetchOnWindowFocus: true,
84
+ refetchOnMount: true,
85
+ refetchOnReconnect: true,
56
86
  queryKey: ['.__process', processId],
57
87
  queryFn: async () => {
58
88
  if (!processId)
@@ -64,9 +94,14 @@ const ClientProcessesProvider = ({ children }) => {
64
94
  // Log and call runner
65
95
  console.warn(`Handled ${process.name} (${process.id})!`);
66
96
  options?.onSuccess?.(process.id, process.name, result);
97
+ // Invalidate query
98
+ await queryClient.invalidateQueries({
99
+ queryKey: ['.__process', processId],
100
+ });
67
101
  return {
68
- id: process.id,
69
- name: process.name,
102
+ dto,
103
+ metadata,
104
+ options,
70
105
  success: true,
71
106
  result,
72
107
  };
@@ -75,14 +110,24 @@ const ClientProcessesProvider = ({ children }) => {
75
110
  if (process?.status === 'pending') {
76
111
  console.log(`Running process ${process.name} (${process.id})...`);
77
112
  options?.onLoading?.(process.id, process.name);
78
- return { id: process.id, name: process.name, pending: true };
113
+ return { dto, metadata, options, pending: true };
79
114
  }
80
115
  else if (process?.status === 'failed') {
81
116
  console.error(`Failed ${process.name} (${process.id}) - ${process.error}.`);
82
- return { id: process.id, name: process.name, success: false };
117
+ // Invalidate query
118
+ await queryClient.invalidateQueries({
119
+ queryKey: ['.__process', processId],
120
+ });
121
+ return {
122
+ dto,
123
+ metadata,
124
+ options,
125
+ success: false,
126
+ error: process.error,
127
+ };
83
128
  }
84
129
  // Return null since no response
85
- return { id: process?.id, name: process?.name, pending: true };
130
+ return { dto, metadata, options, pending: true };
86
131
  },
87
132
  // Returns the next interval length to use to wait for fetch
88
133
  refetchInterval: (query) => {
@@ -118,13 +163,8 @@ const ClientProcessesProvider = ({ children }) => {
118
163
  * @returns
119
164
  */
120
165
  const getProcess = (processId) => {
121
- const clientProcess = clientProcesses.find((clientProcess) => clientProcess.data?.id === processId)?.data ?? null;
122
- return clientProcess?.id
123
- ? {
124
- ...clientProcess,
125
- metadata: clientProcessDtos[clientProcess.id].metadata,
126
- }
127
- : null;
166
+ const clientProcess = clientProcesses.find((clientProcess) => clientProcess.data?.dto?.processId === processId)?.data ?? null;
167
+ return clientProcess ?? null;
128
168
  };
129
169
  /**
130
170
  * Filters processes by the provided attribute.
@@ -133,12 +173,9 @@ const ClientProcessesProvider = ({ children }) => {
133
173
  */
134
174
  const filterProcesses = (filterKey) => {
135
175
  return clientProcesses
136
- .filter((clientProcess) => !!clientProcess.data && !!clientProcess.data.id)
137
- .map((clientProcess) => ({
138
- ...clientProcess.data,
139
- metadata: clientProcessDtos[clientProcess.data.id].metadata,
140
- }))
141
- .filter((clientProcessData) => clientProcessData.metadata.filterKey === filterKey);
176
+ .filter((clientProcess) => !!clientProcess.data?.dto)
177
+ .map((clientProcess) => clientProcess.data)
178
+ .filter((clientProcessData) => clientProcessData?.metadata.filterKey === filterKey);
142
179
  };
143
180
  return (jsxRuntimeExports.jsx(clientProcessesContext.Provider, { value: {
144
181
  registerProcess,
@@ -1 +1 @@
1
- {"version":3,"file":"useClientProcess.js","sources":["../../src/hooks/useClientProcess.tsx"],"sourcesContent":["'use client';\r\n\r\nimport { useState, useRef, createContext, useContext } from 'react';\r\nimport { Query, useQueries, UseQueryResult } 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 id: string;\r\n name: string;\r\n result?: T;\r\n metadata?: ClientProcessMetadata;\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 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 const clientProcesses = useQueries({\r\n queries: Object.entries(clientProcessDtos).map(\r\n ([processId, { dto, options }]) => ({\r\n staleTime: Infinity,\r\n gcTime: Infinity,\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 return {\r\n id: process.id,\r\n name: process.name,\r\n success: true,\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 { id: process.id, name: process.name, 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 return { id: process.id, name: process.name, success: false };\r\n }\r\n\r\n // Return null since no response\r\n return { id: process?.id, name: process?.name, 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?.id === processId,\r\n )?.data ?? null;\r\n\r\n return clientProcess?.id\r\n ? ({\r\n ...clientProcess,\r\n metadata: clientProcessDtos[clientProcess.id].metadata,\r\n } as ClientProcess<T>)\r\n : 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(\r\n (clientProcess) => !!clientProcess.data && !!clientProcess.data.id,\r\n )\r\n .map((clientProcess) => ({\r\n ...clientProcess.data!,\r\n metadata: clientProcessDtos[clientProcess.data!.id!].metadata,\r\n }))\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":";;;;;;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;;;;AAeM;AACA;AACA;;AAEE;AAAgB;;;AAIhB;AACE;;AAOA;AACA;;;;AAKE;;;;;AAMJ;AACE;AACA;AACA;;AACK;AACL;AAGA;;;AAIF;;;AAIF;AACE;AACA;AAAiC;;AAGjC;AACE;AAEF;AAKA;AACA;;AAEH;AAEJ;AAED;;;;;;AAMG;;AAOD;AACE;;AAED;AACH;AAEA;;;;;AAKG;AACH;;;AAOI;AACI;;AAEoB;;AAE5B;AAEA;;;;AAIG;AACH;AACE;AACG;AAGA;;;AAGA;AACA;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 {\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\r\n await queryClient.invalidateQueries({\r\n queryKey: ['.__process', processId],\r\n });\r\n\r\n return {\r\n dto,\r\n metadata,\r\n options,\r\n success: true,\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\r\n await queryClient.invalidateQueries({\r\n queryKey: ['.__process', processId],\r\n });\r\n\r\n return {\r\n dto,\r\n metadata,\r\n options,\r\n success: 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;;;AAIE;AACD;;;;;AAMC;;;;;AAMJ;AACE;AACA;;;AAEK;AACL;;;AAME;AACD;;;;;AAMC;;;;;;;;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;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@betterinternship/components",
3
- "version": "1.5.15",
3
+ "version": "1.5.17",
4
4
  "main": "dist/index.esm.js",
5
5
  "module": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",