diff --git a/src/public/app/services/server.js b/src/public/app/services/server.ts similarity index 71% rename from src/public/app/services/server.js rename to src/public/app/services/server.ts index b8a1d29fb..c24bb86f7 100644 --- a/src/public/app/services/server.js +++ b/src/public/app/services/server.ts @@ -1,13 +1,35 @@ import utils from './utils.js'; import ValidationError from "./validation_error.js"; -async function getHeaders(headers) { +type Headers = Record; + +type Method = string; + +interface Response { + headers: Headers; + body: unknown; +} + +interface Arg extends Response { + statusCode: number; + method: Method; + url: string; + requestId: string; +} + +interface RequestData { + resolve: (value: unknown) => any; + reject: (reason: unknown) => any; + silentNotFound: boolean; +} + +async function getHeaders(headers?: Headers) { const appContext = (await import('../components/app_context.js')).default; const activeNoteContext = appContext.tabManager ? appContext.tabManager.getActiveContext() : null; // headers need to be lowercase because node.js automatically converts them to lower case // also avoiding using underscores instead of dashes since nginx filters them out by default - const allHeaders = { + const allHeaders: Headers = { 'trilium-component-id': glob.componentId, 'trilium-local-now-datetime': utils.localNowDateTime(), 'trilium-hoisted-note-id': activeNoteContext ? activeNoteContext.hoistedNoteId : null, @@ -28,31 +50,31 @@ async function getHeaders(headers) { return allHeaders; } -async function getWithSilentNotFound(url, componentId) { - return await call('GET', url, componentId, { silentNotFound: true }); +async function getWithSilentNotFound(url: string, componentId?: string) { + return await call('GET', url, componentId, { silentNotFound: true }); } -async function get(url, componentId) { - return await call('GET', url, componentId); +async function get(url: string, componentId?: string) { + return await call('GET', url, componentId); } -async function post(url, data, componentId) { - return await call('POST', url, componentId, { data }); +async function post(url: string, data: unknown, componentId?: string) { + return await call('POST', url, componentId, { data }); } -async function put(url, data, componentId) { - return await call('PUT', url, componentId, { data }); +async function put(url: string, data: unknown, componentId?: string) { + return await call('PUT', url, componentId, { data }); } -async function patch(url, data, componentId) { - return await call('PATCH', url, componentId, { data }); +async function patch(url: string, data: unknown, componentId?: string) { + return await call('PATCH', url, componentId, { data }); } -async function remove(url, componentId) { - return await call('DELETE', url, componentId); +async function remove(url: string, componentId?: string) { + return await call('DELETE', url, componentId); } -async function upload(url, fileToUpload) { +async function upload(url: string, fileToUpload: File) { const formData = new FormData(); formData.append('upload', fileToUpload); @@ -68,11 +90,17 @@ async function upload(url, fileToUpload) { } let idCounter = 1; -const idToRequestMap = {}; + +const idToRequestMap: Record = {}; let maxKnownEntityChangeId = 0; -async function call(method, url, componentId, options = {}) { +interface CallOptions { + data?: unknown; + silentNotFound?: boolean; +} + +async function call(method: string, url: string, componentId?: string, options: CallOptions = {}) { let resp; const headers = await getHeaders({ @@ -98,7 +126,7 @@ async function call(method, url, componentId, options = {}) { url: `/${window.glob.baseApiUrl}${url}`, data: data }); - }); + }) as any; } else { resp = await ajax(url, method, data, headers, !!options.silentNotFound); @@ -110,23 +138,25 @@ async function call(method, url, componentId, options = {}) { maxKnownEntityChangeId = Math.max(maxKnownEntityChangeId, parseInt(maxEntityChangeIdStr)); } - return resp.body; + return resp.body as T; } -function ajax(url, method, data, headers, silentNotFound) { +function ajax(url: string, method: string, data: unknown, headers: Headers, silentNotFound: boolean): Promise { return new Promise((res, rej) => { - const options = { + const options: JQueryAjaxSettings = { url: window.glob.baseApiUrl + url, type: method, headers: headers, timeout: 60000, success: (body, textStatus, jqXhr) => { - const respHeaders = {}; + const respHeaders: Headers = {}; jqXhr.getAllResponseHeaders().trim().split(/[\r\n]+/).forEach(line => { const parts = line.split(': '); const header = parts.shift(); - respHeaders[header] = parts.join(': '); + if (header) { + respHeaders[header] = parts.join(': '); + } }); res({ @@ -161,7 +191,7 @@ function ajax(url, method, data, headers, silentNotFound) { if (utils.isElectron()) { const ipc = utils.dynamicRequire('electron').ipcRenderer; - ipc.on('server-response', async (event, arg) => { + ipc.on('server-response', async (event: string, arg: Arg) => { if (arg.statusCode >= 200 && arg.statusCode < 300) { handleSuccessfulResponse(arg); } @@ -178,8 +208,8 @@ if (utils.isElectron()) { delete idToRequestMap[arg.requestId]; }); - function handleSuccessfulResponse(arg) { - if (arg.headers['Content-Type'] === 'application/json') { + function handleSuccessfulResponse(arg: Arg) { + if (arg.headers['Content-Type'] === 'application/json' && typeof arg.body === "string") { arg.body = JSON.parse(arg.body); } @@ -195,13 +225,13 @@ if (utils.isElectron()) { } } -async function reportError(method, url, statusCode, response) { +async function reportError(method: string, url: string, statusCode: number, response: unknown) { let message = response; if (typeof response === 'string') { try { response = JSON.parse(response); - message = response.message; + message = (response as any).message; } catch (e) {} } diff --git a/src/public/app/types.d.ts b/src/public/app/types.d.ts index daf31e929..9934211e9 100644 --- a/src/public/app/types.d.ts +++ b/src/public/app/types.d.ts @@ -21,6 +21,9 @@ interface CustomGlobals { importMarkdownInline: () => Promise; SEARCH_HELP_TEXT: string; activeDialog: JQuery | null; + componentId: string; + csrfToken: string; + baseApiUrl: string; } type RequireMethod = (moduleName: string) => any; diff --git a/tsconfig.json b/tsconfig.json index 1bdd21ef5..ac8d233d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,8 @@ "lib": ["ES2022"], "downlevelIteration": true, "skipLibCheck": true, - "esModuleInterop": true + "esModuleInterop": true, + "allowJs": true }, "include": [ "./src/**/*.js",