@betterinternship/components 1.5.3 → 1.5.5
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.
|
@@ -4,9 +4,10 @@ export interface ProcessCallbackDto {
|
|
|
4
4
|
processCallbackUrl: string;
|
|
5
5
|
}
|
|
6
6
|
export interface ClientProcessOptions {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
filter?: string;
|
|
8
|
+
onLoading?: (processId: string, processName: string) => void;
|
|
9
|
+
onSuccess?: <R>(processId: string, processName: string, result: R) => void;
|
|
10
|
+
onFailure?: (processId: string, processName: string, error?: string) => void;
|
|
10
11
|
}
|
|
11
12
|
export interface ProcessResponse {
|
|
12
13
|
processId: string;
|
|
@@ -20,7 +21,7 @@ export interface ProcessResponse {
|
|
|
20
21
|
*
|
|
21
22
|
* @context
|
|
22
23
|
*/
|
|
23
|
-
export declare const ClientProcessesProvider: ({ children }: {
|
|
24
|
+
export declare const ClientProcessesProvider: ({ children, }: {
|
|
24
25
|
children: React.ReactNode;
|
|
25
26
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
26
27
|
/**
|
|
@@ -29,11 +30,12 @@ export declare const ClientProcessesProvider: ({ children }: {
|
|
|
29
30
|
* @param caller
|
|
30
31
|
* @returns
|
|
31
32
|
*/
|
|
32
|
-
export declare function useClientProcess<T
|
|
33
|
+
export declare function useClientProcess<T>({ caller, filterKey, onLoading, onSuccess, onFailure, }: {
|
|
33
34
|
caller: (inputs: T) => Promise<ProcessResponse>;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
filterKey: string;
|
|
36
|
+
onLoading?: (processId: string, processName: string) => void;
|
|
37
|
+
onSuccess?: <R>(processId: string, processName: string, result: R) => void;
|
|
38
|
+
onFailure?: (processId: string, processName: string, error?: string) => void;
|
|
37
39
|
}): {
|
|
38
40
|
run: (inputs: T) => Promise<{
|
|
39
41
|
id: string;
|
|
@@ -46,4 +48,10 @@ export declare function useClientProcess<T, R>({ caller, loadingMessage, success
|
|
|
46
48
|
result?: unknown;
|
|
47
49
|
success: boolean;
|
|
48
50
|
} | null;
|
|
51
|
+
getAll: () => {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
result?: unknown;
|
|
55
|
+
success: boolean;
|
|
56
|
+
}[];
|
|
49
57
|
};
|
|
@@ -3,7 +3,6 @@ import { j as jsxRuntimeExports } from '../_virtual/jsx-runtime.js';
|
|
|
3
3
|
import { createContext, useState, useRef, useContext } from 'react';
|
|
4
4
|
import { useQueries } from '@tanstack/react-query';
|
|
5
5
|
import { config } from '../config.js';
|
|
6
|
-
import { toast } from 'sonner';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* The orchestrator service to get the process from.
|
|
@@ -12,9 +11,9 @@ import { toast } from 'sonner';
|
|
|
12
11
|
* @returns
|
|
13
12
|
*/
|
|
14
13
|
const processStatusChecker = (processId) => fetch(`${config.orchestratorApi}/${encodeURIComponent(processId)}`, {
|
|
15
|
-
method:
|
|
14
|
+
method: 'GET',
|
|
16
15
|
headers: {
|
|
17
|
-
|
|
16
|
+
'Content-Type': 'application/json',
|
|
18
17
|
},
|
|
19
18
|
}).then((response) => response.json());
|
|
20
19
|
/**
|
|
@@ -24,14 +23,14 @@ const processStatusChecker = (processId) => fetch(`${config.orchestratorApi}/${e
|
|
|
24
23
|
* @returns
|
|
25
24
|
*/
|
|
26
25
|
const processCallbackResultFetcher = (processId, processName, processCallbackUrl) => fetch(`${processCallbackUrl}`, {
|
|
27
|
-
method:
|
|
26
|
+
method: 'POST',
|
|
28
27
|
headers: {
|
|
29
|
-
|
|
28
|
+
'Content-Type': 'application/json',
|
|
30
29
|
},
|
|
31
30
|
body: JSON.stringify({
|
|
32
31
|
processId,
|
|
33
32
|
processName,
|
|
34
|
-
})
|
|
33
|
+
}),
|
|
35
34
|
}).then((response) => response.json());
|
|
36
35
|
/**
|
|
37
36
|
* We need a context to keep track of all client processes.
|
|
@@ -43,40 +42,37 @@ const clientProcessesContext = createContext({});
|
|
|
43
42
|
*
|
|
44
43
|
* @context
|
|
45
44
|
*/
|
|
46
|
-
const ClientProcessesProvider = ({ children }) => {
|
|
45
|
+
const ClientProcessesProvider = ({ children, }) => {
|
|
47
46
|
const [clientProcessDtos, setClientProcessDtos] = useState({});
|
|
48
47
|
const clientProcessAttempts = useRef({});
|
|
49
|
-
const clientProcesses = useQueries({
|
|
48
|
+
const clientProcesses = useQueries({
|
|
49
|
+
queries: Object.entries(clientProcessDtos).map(([processId, { dto, options }]) => ({
|
|
50
50
|
queryKey: ['__process', processId],
|
|
51
51
|
queryFn: async () => {
|
|
52
52
|
if (!processId)
|
|
53
|
-
throw new Error(
|
|
53
|
+
throw new Error('No processId');
|
|
54
54
|
const { process } = await processStatusChecker(processId);
|
|
55
55
|
// Handled! Return the proper response too
|
|
56
|
-
if (process.status ===
|
|
56
|
+
if (process.status === 'handled') {
|
|
57
57
|
const result = await processCallbackResultFetcher(process.id, process.name, dto.processCallbackUrl);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
// Log and call runner
|
|
59
|
+
console.warn(`Handled ${process.name} (${process.id})!`);
|
|
60
|
+
options?.onSuccess?.(process.id, process.name, result);
|
|
61
|
+
return {
|
|
62
|
+
id: process.id,
|
|
63
|
+
name: process.name,
|
|
64
|
+
success: true,
|
|
65
|
+
result,
|
|
66
|
+
};
|
|
64
67
|
}
|
|
65
68
|
// Pending or failed
|
|
66
|
-
if (process.status ===
|
|
67
|
-
console.log(process.name
|
|
68
|
-
|
|
69
|
-
id: process.id,
|
|
70
|
-
duration: Infinity,
|
|
71
|
-
});
|
|
69
|
+
if (process.status === 'pending') {
|
|
70
|
+
console.log(`Running process ${process.name} (${process.id})...`);
|
|
71
|
+
options?.onLoading?.(process.id, process.name);
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
|
-
else if (process.status ===
|
|
75
|
-
console.error(process.name
|
|
76
|
-
toast.error(options?.failureMessage ?? "Something went wrong.", {
|
|
77
|
-
id: process.id
|
|
78
|
-
});
|
|
79
|
-
setTimeout(() => toast.dismiss(process.id), 1500);
|
|
74
|
+
else if (process.status === 'failed') {
|
|
75
|
+
console.error(`Failed ${process.name} (${process.id}) - ${process.error}.`);
|
|
80
76
|
return { id: process.id, name: process.name, success: false };
|
|
81
77
|
}
|
|
82
78
|
// Return null since no response
|
|
@@ -94,7 +90,7 @@ const ClientProcessesProvider = ({ children }) => {
|
|
|
94
90
|
clientProcessAttempts.current[processId] += 1;
|
|
95
91
|
return interval;
|
|
96
92
|
},
|
|
97
|
-
}))
|
|
93
|
+
})),
|
|
98
94
|
});
|
|
99
95
|
/**
|
|
100
96
|
* Registers a new process to handle
|
|
@@ -104,7 +100,10 @@ const ClientProcessesProvider = ({ children }) => {
|
|
|
104
100
|
* @param options
|
|
105
101
|
*/
|
|
106
102
|
const registerProcess = (processId, processCallbackDto, options) => {
|
|
107
|
-
setClientProcessDtos({
|
|
103
|
+
setClientProcessDtos({
|
|
104
|
+
...clientProcessDtos,
|
|
105
|
+
[processId]: { dto: processCallbackDto, options },
|
|
106
|
+
});
|
|
108
107
|
};
|
|
109
108
|
/**
|
|
110
109
|
* Retrieves a process
|
|
@@ -112,12 +111,25 @@ const ClientProcessesProvider = ({ children }) => {
|
|
|
112
111
|
* @param processId
|
|
113
112
|
* @returns
|
|
114
113
|
*/
|
|
115
|
-
const getProcess = (processId) => {
|
|
116
|
-
return clientProcesses.find(clientProcess => clientProcess.data?.id === processId)?.data ?? null;
|
|
114
|
+
const getProcess = (processId, options = {}) => {
|
|
115
|
+
return (clientProcesses.find((clientProcess) => clientProcess.data?.id === processId)?.data ?? null);
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Filters processes by the provided attribute.
|
|
119
|
+
*
|
|
120
|
+
* @param filter
|
|
121
|
+
*/
|
|
122
|
+
const filterProcesses = (filter) => {
|
|
123
|
+
return clientProcesses
|
|
124
|
+
.filter((clientProcess) => clientProcess.data?.id &&
|
|
125
|
+
clientProcessDtos[clientProcess.data.id].options?.filter === filter)
|
|
126
|
+
.map((clientProcess) => clientProcess.data)
|
|
127
|
+
.filter((clientProcess) => !!clientProcess);
|
|
117
128
|
};
|
|
118
129
|
return (jsxRuntimeExports.jsx(clientProcessesContext.Provider, { value: {
|
|
119
130
|
registerProcess,
|
|
120
|
-
getProcess
|
|
131
|
+
getProcess,
|
|
132
|
+
filterProcesses,
|
|
121
133
|
}, children: children }));
|
|
122
134
|
};
|
|
123
135
|
/**
|
|
@@ -126,7 +138,7 @@ const ClientProcessesProvider = ({ children }) => {
|
|
|
126
138
|
* @param caller
|
|
127
139
|
* @returns
|
|
128
140
|
*/
|
|
129
|
-
function useClientProcess({ caller,
|
|
141
|
+
function useClientProcess({ caller, filterKey, onLoading, onSuccess, onFailure, }) {
|
|
130
142
|
const clientProcessesApi = useContext(clientProcessesContext);
|
|
131
143
|
// Note that the exposed function doesn't return process ids
|
|
132
144
|
// Client should be oblivious to those
|
|
@@ -134,9 +146,9 @@ function useClientProcess({ caller, loadingMessage, successMessage, failureMessa
|
|
|
134
146
|
const wrappedCaller = async (inputs) => {
|
|
135
147
|
const response = await caller(inputs);
|
|
136
148
|
clientProcessesApi.registerProcess(response.processId, response, {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
149
|
+
onLoading,
|
|
150
|
+
onSuccess,
|
|
151
|
+
onFailure,
|
|
140
152
|
});
|
|
141
153
|
return {
|
|
142
154
|
id: response.processId,
|
|
@@ -146,7 +158,8 @@ function useClientProcess({ caller, loadingMessage, successMessage, failureMessa
|
|
|
146
158
|
};
|
|
147
159
|
return {
|
|
148
160
|
run: wrappedCaller,
|
|
149
|
-
get: (id) => clientProcessesApi.getProcess(id)
|
|
161
|
+
get: (id) => clientProcessesApi.getProcess(id),
|
|
162
|
+
getAll: () => clientProcessesApi.filterProcesses(filterKey),
|
|
150
163
|
};
|
|
151
164
|
}
|
|
152
165
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useClientProcess.js","sources":["../../src/hooks/useClientProcess.tsx"],"sourcesContent":["
|
|
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 } 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\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 filter?: 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\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 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 * 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 options?: ClientProcessOptions,\r\n ) => void;\r\n getProcess: (\r\n processId: string,\r\n ) => { id: string; name: string; result?: unknown; success: boolean } | null;\r\n filterProcesses: (\r\n filter: string,\r\n ) => { id: string; name: string; result?: unknown; success: boolean }[];\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 = ({\r\n children,\r\n}: {\r\n children: React.ReactNode;\r\n}) => {\r\n const [clientProcessDtos, setClientProcessDtos] = useState<\r\n Record<string, { dto: ProcessCallbackDto; options?: ClientProcessOptions }>\r\n >({});\r\n const clientProcessAttempts = useRef<Record<string, number>>({});\r\n const clientProcesses = useQueries({\r\n queries: Object.entries(clientProcessDtos).map(\r\n ([processId, { dto, options }]) => ({\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 null;\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 null;\r\n },\r\n\r\n // Returns the next interval length to use to wait for fetch\r\n refetchInterval: (query: Query) => {\r\n const data = query.state.data;\r\n if (data !== null) 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 options?: ClientProcessOptions,\r\n ) => {\r\n setClientProcessDtos({\r\n ...clientProcessDtos,\r\n [processId]: { dto: processCallbackDto, 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 = (processId: string, options: { filter?: string } = {}) => {\r\n return (\r\n clientProcesses.find(\r\n (clientProcess) => clientProcess.data?.id === processId,\r\n )?.data ?? null\r\n );\r\n };\r\n\r\n /**\r\n * Filters processes by the provided attribute.\r\n *\r\n * @param filter\r\n */\r\n const filterProcesses = (filter: string) => {\r\n return clientProcesses\r\n .filter(\r\n (clientProcess) =>\r\n clientProcess.data?.id &&\r\n clientProcessDtos[clientProcess.data.id].options?.filter === filter,\r\n )\r\n .map((clientProcess) => clientProcess.data)\r\n .filter((clientProcess) => !!clientProcess);\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 }}\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 (inputs: T) => {\r\n const response = await caller(inputs);\r\n clientProcessesApi.registerProcess(response.processId, response, {\r\n onLoading,\r\n onSuccess,\r\n onFailure,\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 run: wrappedCaller,\r\n get: (id: string) => clientProcessesApi.getProcess(id),\r\n getAll: () => clientProcessesApi.filterProcesses(filterKey),\r\n };\r\n}"],"names":[],"mappings":";;;;;;AAsCA;;;;;AAKG;AACH;AAEI;AACA;AACE;AACD;AACF;AAIH;;;;;AAKG;AACH;AAMI;AACA;AACE;AACD;AACD;;;;AAID;AAgBH;;;AAGG;AACH;AAIA;;;;AAIG;;;AASD;;;AAIM;;AAEE;AAAgB;;;AAIhB;AACE;;AAOA;AACA;;;;AAKE;;;;;AAMJ;AACE;AACA;AACA;;AACK;AACL;AAGA;;;AAIF;;;AAIF;AACE;;AACmB;;AAGnB;AACE;AAEF;AAKA;AACA;;AAEH;AAEJ;AAED;;;;;;AAMG;;AAMD;AACE;;AAED;AACH;AAEA;;;;;AAKG;;;AAOH;AAEA;;;;AAIG;AACH;AACE;;AAIM;;;AAIR;AAEA;;;;;AAWF;AAEA;;;;;AAKG;AACG;AAaJ;;;;AAKA;AACE;;;;;AAKC;;;;;;AAOH;;AAGE;;;;AAIJ;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@betterinternship/components",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.5",
|
|
4
4
|
"main": "dist/index.esm.js",
|
|
5
5
|
"module": "dist/index.esm.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -74,6 +74,13 @@
|
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
+
"@eslint/eslintrc": "^3.2.0",
|
|
78
|
+
"@eslint/js": "^9.18.0",
|
|
79
|
+
"eslint": "^9.18.0",
|
|
80
|
+
"eslint-config-prettier": "^10.0.1",
|
|
81
|
+
"eslint-plugin-prettier": "^5.2.2",
|
|
82
|
+
"prettier": "^3.4.2",
|
|
83
|
+
"typescript-eslint": "^8.20.0",
|
|
77
84
|
"@radix-ui/react-accordion": "^1.2.11",
|
|
78
85
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
79
86
|
"@radix-ui/react-aspect-ratio": "^1.1.1",
|