From acb3030d56266ebe20ef631385135f52930cf3d0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 26 Mar 2026 20:56:14 +0200 Subject: [PATCH] chore(core): fix most bootstrap-related type errors --- .../src/lightweight/browser_routes.ts | 49 ++++++++++--------- apps/client/src/services/ws.ts | 11 ++--- apps/client/src/types.d.ts | 2 +- apps/server/src/routes/index.ts | 37 +++++++------- packages/commons/src/lib/server_api.ts | 24 +++------ .../src/services/bootstrap_utils.ts | 16 +++--- 6 files changed, 70 insertions(+), 69 deletions(-) diff --git a/apps/client-standalone/src/lightweight/browser_routes.ts b/apps/client-standalone/src/lightweight/browser_routes.ts index a050692ff8..dc6868145e 100644 --- a/apps/client-standalone/src/lightweight/browser_routes.ts +++ b/apps/client-standalone/src/lightweight/browser_routes.ts @@ -241,34 +241,19 @@ function bootstrapRoute(): BootstrapDefinition { const assetPath = "."; const isDbInitialized = sql_init.isDbInitialized(); - const commonItems = getSharedBootstrapItems(assetPath, isDbInitialized); - - if (!isDbInitialized) { - return { - ...commonItems, - isStandalone: true, - baseApiUrl: "../api/", - }; - } - - return { - ...commonItems, - appPath: assetPath, - device: false, // Let the client detect device type. - csrfToken: "dummy-csrf-token", - themeCssUrl: false, - themeUseNextAsBase: "next", - triliumVersion: packageJson.version, - baseApiUrl: "../api/", - headingStyle: "plain", - layoutOrientation: "vertical", - platform: "web", + const commonItems = { + ...getSharedBootstrapItems(assetPath, isDbInitialized), isDev: import.meta.env.DEV, + isStandalone: true, + themeCssUrl: false as const, + themeUseNextAsBase: "next" as const, isMainWindow: true, isElectron: false, - isStandalone: true, hasNativeTitleBar: false, hasBackgroundEffects: false, + triliumVersion: packageJson.version, + device: false as const, // Let the client detect device type. + appPath: assetPath, // TODO: Fill properly currentLocale: { id: "en", name: "English", rtl: false }, @@ -277,6 +262,24 @@ function bootstrapRoute(): BootstrapDefinition { appCssNoteIds: [], TRILIUM_SAFE_MODE: false }; + + if (!isDbInitialized) { + return { + ...commonItems, + isStandalone: true, + baseApiUrl: "../api/", + isProtectedSessionAvailable: false, + }; + } + + return { + ...commonItems, + csrfToken: "dummy-csrf-token", + baseApiUrl: "../api/", + headingStyle: "plain", + layoutOrientation: "vertical", + platform: "web", + }; } /** diff --git a/apps/client/src/services/ws.ts b/apps/client/src/services/ws.ts index e014974761..6146fc9b7e 100644 --- a/apps/client/src/services/ws.ts +++ b/apps/client/src/services/ws.ts @@ -7,16 +7,15 @@ import { t } from "./i18n.js"; import options from "./options.js"; import server from "./server.js"; import toastService from "./toast.js"; -import toast from "./toast.js"; import utils from "./utils.js"; type MessageHandler = (message: WebSocketMessage) => void; let messageHandlers: MessageHandler[] = []; let ws: WebSocket; -let lastAcceptedEntityChangeId = window.glob.maxEntityChangeIdAtLoad; -let lastAcceptedEntityChangeSyncId = window.glob.maxEntityChangeSyncIdAtLoad; -let lastProcessedEntityChangeId = window.glob.maxEntityChangeIdAtLoad; +let lastAcceptedEntityChangeId = window.glob.maxEntityChangeIdAtLoad ?? 0; +let lastAcceptedEntityChangeSyncId = window.glob.maxEntityChangeSyncIdAtLoad ?? 0; +let lastProcessedEntityChangeId = window.glob.maxEntityChangeIdAtLoad ?? 0; let lastPingTs: number; let frontendUpdateDataQueue: EntityChange[] = []; @@ -261,7 +260,7 @@ async function sendPing() { if (Date.now() - lastPingTs > 30000) { console.warn(utils.now(), "Lost websocket connection to the backend"); - toast.showPersistent({ + toastService.showPersistent({ id: "lost-websocket-connection", title: t("ws.lost-websocket-connection-title"), message: t("ws.lost-websocket-connection-message"), @@ -270,7 +269,7 @@ async function sendPing() { } if (ws.readyState === ws.OPEN) { - toast.closePersistent("lost-websocket-connection"); + toastService.closePersistent("lost-websocket-connection"); ws.send( JSON.stringify({ type: "ping", diff --git a/apps/client/src/types.d.ts b/apps/client/src/types.d.ts index 9891c1472f..545ff8e029 100644 --- a/apps/client/src/types.d.ts +++ b/apps/client/src/types.d.ts @@ -15,7 +15,7 @@ interface ElectronProcess { platform: string; } -interface CustomGlobals extends Extract { +interface CustomGlobals extends BootstrapDefinition { isDesktop: typeof utils.isDesktop; isMobile: typeof utils.isMobile; getComponentByEl: typeof appContext.getComponentByEl; diff --git a/apps/server/src/routes/index.ts b/apps/server/src/routes/index.ts index f1ce6cd8cd..f63d382023 100644 --- a/apps/server/src/routes/index.ts +++ b/apps/server/src/routes/index.ts @@ -10,7 +10,7 @@ import attributeService from "../services/attributes.js"; import config from "../services/config.js"; import log from "../services/log.js"; import optionService from "../services/options.js"; -import { isDev, isElectron, isMac, isWindows11 } from "../services/utils.js"; +import { isDev, isElectron, isMac, isWindows, isWindows11 } from "../services/utils.js"; import { generateCsrfToken } from "./csrf_protection.js"; type View = "desktop" | "mobile" | "print"; @@ -25,14 +25,29 @@ export function bootstrap(req: Request, res: Response) { req.session.csrfInitialized = true; } + const view = getView(req); const isDbInitialized = sql_init.isDbInitialized(); - const commonItems = getSharedBootstrapItems(assetPath, isDbInitialized); + const commonItems = { + ...getSharedBootstrapItems(assetPath, isDbInitialized), + baseApiUrl: "api/", + appPath, + isStandalone: false, + isElectron, + isDev, + triliumVersion: packageJson.version, + device: view, + TRILIUM_SAFE_MODE: !!process.env.TRILIUM_SAFE_MODE, + instanceName: config.General ? config.General.instanceName : null + }; if (!isDbInitialized) { res.send({ ...commonItems, - baseApiUrl: "api/", - componentId: "" - }); + hasNativeTitleBar: false, + hasBackgroundEffects: isElectron && (isWindows11 || isMac), + isMainWindow: true, + appCssNoteIds: [], + themeCssUrl: false as const + } satisfies BootstrapDefinition); return; } @@ -44,43 +59,31 @@ export function bootstrap(req: Request, res: Response) { }); log.info(`CSRF token generation: ${csrfToken ? "Successful" : "Failed"}`); - const view = getView(req); const theme = options.theme; const themeNote = attributeService.getNoteWithLabel("appTheme", theme); const nativeTitleBarVisible = options.nativeTitleBarVisible === "true"; const iconPacks = iconPackService.getIconPacks(); - const sql = getSql(); res.send({ ...commonItems, dbInitialized: true, - device: view, csrfToken, themeCssUrl: getThemeCssUrl(theme, themeNote), themeUseNextAsBase: themeNote?.getAttributeValue("label", "appThemeBase") as "next" | "next-light" | "next-dark", platform: process.platform, - isElectron, hasNativeTitleBar: isElectron && nativeTitleBarVisible, hasBackgroundEffects: options.backgroundEffects === "true" && isElectron && (isWindows11 || isMac) && !nativeTitleBarVisible, - maxEntityChangeIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes"), - maxEntityChangeSyncIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes WHERE isSynced = 1"), - instanceName: config.General ? config.General.instanceName : null, appCssNoteIds: getAppCssNoteIds(), - isDev, isMainWindow: view === "mobile" ? true : !req.query.extraWindow, - triliumVersion: packageJson.version, - appPath, - baseApiUrl: 'api/', iconPackCss: iconPacks .map((p: iconPackService.ProcessedIconPack) => iconPackService.generateCss(p, p.builtin ? `${assetPath}/fonts/${p.fontAttachmentId}.${iconPackService.MIME_TO_EXTENSION_MAPPINGS[p.fontMime]}` : `api/attachments/download/${p.fontAttachmentId}`)) .filter(Boolean) .join("\n\n"), - TRILIUM_SAFE_MODE: !!process.env.TRILIUM_SAFE_MODE } satisfies BootstrapDefinition); } diff --git a/packages/commons/src/lib/server_api.ts b/packages/commons/src/lib/server_api.ts index bf5c173cfe..cb50e57a37 100644 --- a/packages/commons/src/lib/server_api.ts +++ b/packages/commons/src/lib/server_api.ts @@ -313,9 +313,9 @@ export interface DefinitionObject { } /** - * Subset of bootstrap items that are available both in the main client and in the setup page. + * Bootstrap items that the client needs to start up. These are sent by the server in the HTML and made available as `window.glob`. */ -export interface BootstrapCommonItems { +export type BootstrapDefinition = { dbInitialized: boolean; baseApiUrl: string; assetPath: string; @@ -323,24 +323,17 @@ export interface BootstrapCommonItems { themeUseNextAsBase?: "next" | "next-light" | "next-dark"; iconPackCss: string; iconRegistry: IconRegistry; -} - -/** - * Bootstrap items that the client needs to start up. These are sent by the server in the HTML and made available as `window.glob`. - */ -export type BootstrapDefinition = BootstrapCommonItems & ({ - dbInitialized: true; device: "mobile" | "desktop" | "print" | false; - csrfToken: string; + csrfToken?: string; headingStyle: "plain" | "underline" | "markdown"; layoutOrientation: "vertical" | "horizontal"; platform?: typeof process.platform | "web"; isElectron: boolean; - isStandalone?: boolean; + isStandalone: boolean; hasNativeTitleBar: boolean; hasBackgroundEffects: boolean; - maxEntityChangeIdAtLoad: number; - maxEntityChangeSyncIdAtLoad: number; + maxEntityChangeIdAtLoad?: number; + maxEntityChangeSyncIdAtLoad?: number; instanceName: string | null; appCssNoteIds: string[]; isDev: boolean; @@ -351,9 +344,8 @@ export type BootstrapDefinition = BootstrapCommonItems & ({ currentLocale: Locale; isRtl: boolean; TRILIUM_SAFE_MODE: boolean; -} | { - dbInitialized: false; -}); + componentId?: string; +}; /** * Response for /api/setup/status. diff --git a/packages/trilium-core/src/services/bootstrap_utils.ts b/packages/trilium-core/src/services/bootstrap_utils.ts index c541407f4a..7f41145870 100644 --- a/packages/trilium-core/src/services/bootstrap_utils.ts +++ b/packages/trilium-core/src/services/bootstrap_utils.ts @@ -12,14 +12,20 @@ export default function getSharedBootstrapItems(assetPath: string, dbInitialized const commonItems = { assetPath, dbInitialized, + currentLocale, + isRtl: !!currentLocale.rtl, + isProtectedSessionAvailable: false, + layoutOrientation: "vertical" as const, + headingStyle: "plain" as const, + componentId: "", ...getIconConfig(assetPath) }; if (!dbInitialized) { return { ...commonItems, - themeCssUrl: false, - themeUseNextAsBase: "next" + themeCssUrl: false as const, + themeUseNextAsBase: "next" as const }; } @@ -27,11 +33,9 @@ export default function getSharedBootstrapItems(assetPath: string, dbInitialized ...commonItems, headingStyle: options.getOption("headingStyle") as "plain" | "underline" | "markdown", layoutOrientation: options.getOption("layoutOrientation") as "vertical" | "horizontal", - maxEntityChangeIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes"), - maxEntityChangeSyncIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes WHERE isSynced = 1"), + maxEntityChangeIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes"), + maxEntityChangeSyncIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes WHERE isSynced = 1"), isProtectedSessionAvailable: protected_session.isProtectedSessionAvailable(), - currentLocale, - isRtl: !!currentLocale.rtl, } }