1
+let _extensionsModule = null;
2
+
3
+async function _getExtensions() {
4
+ if (!_extensionsModule) _extensionsModule = await import("./extensions.js");
5
+ return _extensionsModule;
6
+}
7
+
8
+async function _shouldCallApiExtensions(apiUrl) {
9
+ const extensions = await _getExtensions();
10
+ const excluded = extensions.API_EXTENSION_EXCLUDED_ENDPOINTS;
11
+ return !(excluded instanceof Set && excluded.has(apiUrl));
12
+}
13
+
14
+function _normalizeApiUrl(url) {
15
+ return url.startsWith("/api/") || url.startsWith("api/")
16
+ ? `/${url.replace(/^\/+/, "")}`
17
+ : `/api/${url.replace(/^\/+/, "")}`;
18
+}
19
+
20
/**
21
* Call a JSON-in JSON-out API endpoint
22
* Data is automatically serialized
25
* @returns {Promise<any>} The JSON response from the API
26
*/
27
export async function callJsonApi(endpoint, data) {
9
- const response = await fetchApi(endpoint, {
28
+ const apiUrl = _normalizeApiUrl(endpoint);
29
+
30
+ /** @type {{ endpoint: string, data: any, response: Response | null, result: any, error: Error | null }} */
31
+ const ctx = {
32
+ endpoint,
33
+ data,
34
+ response: null,
35
+ result: null,
36
+ error: null,
37
+ };
38
+
39
+ if (await _shouldCallApiExtensions(apiUrl)) {
40
+ const extensions = await _getExtensions();
41
+ await extensions.callJsExtensions("json_api_call_before", ctx);
42
+ }
43
+
44
+ const response = await fetchApi(ctx.endpoint, {
45
method: "POST",
46
headers: {
47
"Content-Type": "application/json",
48
},
49
credentials: "same-origin",
15
- body: JSON.stringify(data),
50
+ body: JSON.stringify(ctx.data),
51
});
52
+ ctx.response = response;
53
54
if (!response.ok) {
55
const error = await response.text();
20
- throw new Error(error);
56
+ ctx.error = new Error(error);
57
+
58
+ if (await _shouldCallApiExtensions(apiUrl)) {
59
+ const extensions = await _getExtensions();
60
+ await extensions.callJsExtensions("json_api_call_error", ctx);
61
+ }
62
+
63
+ if (ctx.error) throw ctx.error;
64
+
65
+ return ctx.result;
66
+ }
67
+
68
+ ctx.result = await response.json();
69
+
70
+ if (await _shouldCallApiExtensions(apiUrl)) {
71
+ const extensions = await _getExtensions();
72
+ await extensions.callJsExtensions("json_api_call_after", ctx);
73
}
22
- const jsonResponse = await response.json();
23
- return jsonResponse;
74
+
75
+ return ctx.result;
76
}
77
78
/**
83
* @returns {Promise<Response>} The fetch response
84
*/
85
export async function fetchApi(url, request) {
34
- async function _getExtensions() {
35
- try {
36
- return await import("./extensions.js");
37
- } catch {
38
- return null;
39
- }
40
- }
41
-
42
- /**
43
- * @param {string} apiUrl
44
- * @returns {Promise<boolean>}
45
- */
46
- async function _shouldCallApiExtensions(apiUrl) {
47
- const extensions = await _getExtensions();
48
- if (!extensions) return false;
49
- const excluded = extensions.API_EXTENSION_EXCLUDED_ENDPOINTS;
50
- return !(excluded instanceof Set && excluded.has(apiUrl));
51
- }
52
-
86
async function _wrap(retry) {
87
// get the CSRF token
88
const token = await getCsrfToken();
97
finalRequest.headers["X-CSRF-Token"] = token;
98
99
// perform the fetch with the updated request
67
- const apiUrl = url.startsWith('/api/') || url.startsWith('api/') ? `/${url.replace(/^\/+/, '')}` : `/api/${url.replace(/^\/+/, '')}`;
100
+ const apiUrl = _normalizeApiUrl(url);
101
102
/** @type {{ url: string, apiUrl: string, request: any, response: Response | null, retry: boolean }} */
103
const ctx = {
110
111
if (await _shouldCallApiExtensions(apiUrl)) {
112
const extensions = await _getExtensions();
80
- if (extensions) {
81
- await extensions.callJsExtensions("api_call_before", ctx);
82
- }
113
+ await extensions.callJsExtensions("fetch_api_call_before", ctx);
114
}
115
85
- const response = ctx.response || await fetch(ctx.apiUrl, ctx.request);
116
+ const response = ctx.response || (await fetch(ctx.apiUrl, ctx.request));
117
ctx.response = response;
118
119
if (await _shouldCallApiExtensions(apiUrl)) {
120
const extensions = await _getExtensions();
90
- if (extensions) {
91
- await extensions.callJsExtensions("api_call_after", ctx);
92
- }
121
+ await extensions.callJsExtensions("fetch_api_call_after", ctx);
122
}
123
124
const finalResponse = ctx.response;