chore(core): fix most bootstrap-related type errors

This commit is contained in:
Elian Doran
2026-03-26 20:56:14 +02:00
parent 0fc5b2e997
commit acb3030d56
6 changed files with 70 additions and 69 deletions

View File

@@ -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",
};
}
/**

View File

@@ -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",

View File

@@ -15,7 +15,7 @@ interface ElectronProcess {
platform: string;
}
interface CustomGlobals extends Extract<BootstrapDefinition, { dbInitialized: true }> {
interface CustomGlobals extends BootstrapDefinition {
isDesktop: typeof utils.isDesktop;
isMobile: typeof utils.isMobile;
getComponentByEl: typeof appContext.getComponentByEl;

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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<number>("SELECT COALESCE(MAX(id), 0) FROM entity_changes"),
maxEntityChangeSyncIdAtLoad: sql.getValue<number>("SELECT COALESCE(MAX(id), 0) FROM entity_changes WHERE isSynced = 1"),
isProtectedSessionAvailable: protected_session.isProtectedSessionAvailable(),
currentLocale,
isRtl: !!currentLocale.rtl,
}
}