@betterinternship/components 1.5.3 → 1.5.4
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,9 @@ export interface ProcessCallbackDto {
|
|
|
4
4
|
processCallbackUrl: string;
|
|
5
5
|
}
|
|
6
6
|
export interface ClientProcessOptions {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
onLoading?: (processId: string, processName: string) => void;
|
|
8
|
+
onSuccess?: <R>(processId: string, processName: string, result: R) => void;
|
|
9
|
+
onFailure?: (processId: string, processName: string, error?: string) => void;
|
|
10
10
|
}
|
|
11
11
|
export interface ProcessResponse {
|
|
12
12
|
processId: string;
|
|
@@ -20,7 +20,7 @@ export interface ProcessResponse {
|
|
|
20
20
|
*
|
|
21
21
|
* @context
|
|
22
22
|
*/
|
|
23
|
-
export declare const ClientProcessesProvider: ({ children }: {
|
|
23
|
+
export declare const ClientProcessesProvider: ({ children, }: {
|
|
24
24
|
children: React.ReactNode;
|
|
25
25
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
26
26
|
/**
|
|
@@ -29,11 +29,11 @@ export declare const ClientProcessesProvider: ({ children }: {
|
|
|
29
29
|
* @param caller
|
|
30
30
|
* @returns
|
|
31
31
|
*/
|
|
32
|
-
export declare function useClientProcess<T, R>({ caller,
|
|
32
|
+
export declare function useClientProcess<T, R>({ caller, onLoading, onSuccess, onFailure, }: {
|
|
33
33
|
caller: (inputs: T) => Promise<ProcessResponse>;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
onLoading?: (processId: string, processName: string) => void;
|
|
35
|
+
onSuccess?: (processId: string, processName: string) => void;
|
|
36
|
+
onFailure?: (processId: string, processName: string) => void;
|
|
37
37
|
}): {
|
|
38
38
|
run: (inputs: T) => Promise<{
|
|
39
39
|
id: string;
|
|
@@ -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
|
|
@@ -113,11 +112,11 @@ const ClientProcessesProvider = ({ children }) => {
|
|
|
113
112
|
* @returns
|
|
114
113
|
*/
|
|
115
114
|
const getProcess = (processId) => {
|
|
116
|
-
return clientProcesses.find(clientProcess => clientProcess.data?.id === processId)?.data ?? null;
|
|
115
|
+
return (clientProcesses.find((clientProcess) => clientProcess.data?.id === processId)?.data ?? null);
|
|
117
116
|
};
|
|
118
117
|
return (jsxRuntimeExports.jsx(clientProcessesContext.Provider, { value: {
|
|
119
118
|
registerProcess,
|
|
120
|
-
getProcess
|
|
119
|
+
getProcess,
|
|
121
120
|
}, children: children }));
|
|
122
121
|
};
|
|
123
122
|
/**
|
|
@@ -126,7 +125,7 @@ const ClientProcessesProvider = ({ children }) => {
|
|
|
126
125
|
* @param caller
|
|
127
126
|
* @returns
|
|
128
127
|
*/
|
|
129
|
-
function useClientProcess({ caller,
|
|
128
|
+
function useClientProcess({ caller, onLoading, onSuccess, onFailure, }) {
|
|
130
129
|
const clientProcessesApi = useContext(clientProcessesContext);
|
|
131
130
|
// Note that the exposed function doesn't return process ids
|
|
132
131
|
// Client should be oblivious to those
|
|
@@ -134,9 +133,9 @@ function useClientProcess({ caller, loadingMessage, successMessage, failureMessa
|
|
|
134
133
|
const wrappedCaller = async (inputs) => {
|
|
135
134
|
const response = await caller(inputs);
|
|
136
135
|
clientProcessesApi.registerProcess(response.processId, response, {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
onLoading,
|
|
137
|
+
onSuccess,
|
|
138
|
+
onFailure,
|
|
140
139
|
});
|
|
141
140
|
return {
|
|
142
141
|
id: response.processId,
|
|
@@ -146,7 +145,7 @@ function useClientProcess({ caller, loadingMessage, successMessage, failureMessa
|
|
|
146
145
|
};
|
|
147
146
|
return {
|
|
148
147
|
run: wrappedCaller,
|
|
149
|
-
get: (id) => clientProcessesApi.getProcess(id)
|
|
148
|
+
get: (id) => clientProcessesApi.getProcess(id),
|
|
150
149
|
};
|
|
151
150
|
}
|
|
152
151
|
|
|
@@ -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\nimport { toast } from 'sonner';\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 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}\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) => {\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 return (\r\n <clientProcessesContext.Provider\r\n value={{\r\n registerProcess,\r\n getProcess,\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>({\r\n caller,\r\n onLoading,\r\n onSuccess,\r\n onFailure,\r\n}: {\r\n caller: (inputs: T) => Promise<ProcessResponse>;\r\n onLoading?: (processId: string, processName: string) => void;\r\n onSuccess?: (processId: string, processName: string) => void;\r\n onFailure?: (processId: string, processName: 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 };\r\n}"],"names":[],"mappings":";;;;;;AAsCA;;;;;AAKG;AACH;AAEI;AACA;AACE;AACD;AACF;AAIH;;;;;AAKG;AACH;AAMI;AACA;AACE;AACD;AACD;;;;AAID;AAaH;;;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;AACH;;AAMA;AAEA;;;;AAUF;AAEA;;;;;AAKG;AACG;AAWJ;;;;AAKA;AACE;;;;;AAKC;;;;;;AAOH;;AAGE;;;AAGJ;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@betterinternship/components",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.4",
|
|
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",
|