diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 0fabd8a74..e4310dc50 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -39,7 +39,7 @@ jobs: - uses: nrwl/nx-set-shas@v4 - name: Check affected - run: pnpm nx affected -t build rebuild-deps + run: pnpm nx affected -t typecheck build rebuild-deps report-electron-size: name: Report Electron size diff --git a/.vscode/settings.json b/.vscode/settings.json index 86eca8e3a..5a2764983 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,5 +26,7 @@ ".github/workflows/nightly.yml" ], "typescript.validate.enable": true, - "typescript.tsserver.experimental.enableProjectDiagnostics": true + "typescript.tsserver.experimental.enableProjectDiagnostics": true, + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true } \ No newline at end of file diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index b64678011..f7a5dfb1e 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -27,6 +27,7 @@ import type EditableTextTypeWidget from "../widgets/type_widgets/editable_text.j import type { NativeImage, TouchBar } from "electron"; import TouchBarComponent from "./touch_bar.js"; import type { CKTextEditor } from "@triliumnext/ckeditor5"; +import type CodeMirror from "@triliumnext/codemirror"; interface Layout { getRootWidget: (appContext: AppContext) => RootWidget; @@ -191,7 +192,7 @@ export type CommandMappings = { ExecuteCommandData & { callback?: GetTextEditorCallback; }; - executeWithCodeEditor: CommandData & ExecuteCommandData; + executeWithCodeEditor: CommandData & ExecuteCommandData; /** * Called upon when attempting to retrieve the content element of a {@link NoteContext}. * Generally should not be invoked manually, as it is used by {@link NoteContext.getContentElement}. diff --git a/apps/client/src/components/touch_bar.ts b/apps/client/src/components/touch_bar.ts index 4afe65151..7bf10d7f1 100644 --- a/apps/client/src/components/touch_bar.ts +++ b/apps/client/src/components/touch_bar.ts @@ -54,7 +54,7 @@ export default class TouchBarComponent extends Component { #refreshTouchBar() { const { TouchBar } = this.remote; const parentComponent = this.lastFocusedComponent; - let touchBar = null; + let touchBar: Electron.CrossProcessExports.TouchBar | null = null; if (this.$activeModal?.length) { touchBar = this.#buildModalTouchBar(); diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts index e968dcae9..9e215821d 100644 --- a/apps/client/src/entities/fnote.ts +++ b/apps/client/src/entities/fnote.ts @@ -789,7 +789,7 @@ class FNote { */ async getRelationTargets(name: string) { const relations = this.getRelations(name); - const targets = []; + const targets: (FNote | null)[] = []; for (const relation of relations) { targets.push(await this.froca.getNote(relation.value)); diff --git a/apps/client/src/services/clipboard.ts b/apps/client/src/services/clipboard.ts index 2da2d1cf3..002309586 100644 --- a/apps/client/src/services/clipboard.ts +++ b/apps/client/src/services/clipboard.ts @@ -80,7 +80,7 @@ async function copy(branchIds: string[]) { if (utils.isElectron()) { // https://github.com/zadam/trilium/issues/2401 const { clipboard } = require("electron"); - const links = []; + const links: string[] = []; for (const branch of froca.getBranches(clipboardBranchIds)) { const $link = await linkService.createLink(`${branch.parentNoteId}/${branch.noteId}`, { referenceLink: true }); diff --git a/apps/client/src/services/froca_updater.ts b/apps/client/src/services/froca_updater.ts index 37f9d2814..1f8eaa541 100644 --- a/apps/client/src/services/froca_updater.ts +++ b/apps/client/src/services/froca_updater.ts @@ -50,7 +50,7 @@ async function processEntityChanges(entityChanges: EntityChange[]) { // To this we count: standard parent-child relationships and template/inherit relations (attribute inheritance follows them). // Here we watch for changes which might violate this principle - e.g., an introduction of a new "inherit" relation might // mean we need to load the target of the relation (and then perhaps transitively the whole note path of this target). - const missingNoteIds = []; + const missingNoteIds: string[] = []; for (const { entityName, entity } of entityChanges) { if (!entity) { diff --git a/apps/client/src/services/link.ts b/apps/client/src/services/link.ts index 3dac7688f..0425652f6 100644 --- a/apps/client/src/services/link.ts +++ b/apps/client/src/services/link.ts @@ -215,9 +215,9 @@ export function parseNavigationStateFromUrl(url: string | undefined) { const viewScope: ViewScope = { viewMode: "default" }; - let ntxId = null; - let hoistedNoteId = null; - let searchString = null; + let ntxId: string | null = null; + let hoistedNoteId: string | null = null; + let searchString: string | null = null; if (paramString) { for (const pair of paramString.split("&")) { diff --git a/apps/client/src/services/promoted_attribute_definition_parser.ts b/apps/client/src/services/promoted_attribute_definition_parser.ts index e40c24bbc..f46040e2a 100644 --- a/apps/client/src/services/promoted_attribute_definition_parser.ts +++ b/apps/client/src/services/promoted_attribute_definition_parser.ts @@ -1,7 +1,7 @@ type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url"; type Multiplicity = "single" | "multi"; -interface DefinitionObject { +export interface DefinitionObject { isPromoted?: boolean; labelType?: LabelType; multiplicity?: Multiplicity; diff --git a/apps/client/src/services/syntax_highlight.ts b/apps/client/src/services/syntax_highlight.ts index bfe887b9d..cb2d4d873 100644 --- a/apps/client/src/services/syntax_highlight.ts +++ b/apps/client/src/services/syntax_highlight.ts @@ -97,7 +97,7 @@ export function loadHighlightingTheme(themeName: string) { */ export function isSyntaxHighlightEnabled() { const theme = options.get("codeBlockTheme"); - return theme && theme !== "none"; + return !!theme && theme !== "none"; } /** diff --git a/apps/client/src/services/tree.ts b/apps/client/src/services/tree.ts index 485447214..c508654f5 100644 --- a/apps/client/src/services/tree.ts +++ b/apps/client/src/services/tree.ts @@ -34,8 +34,8 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root path.push("root"); } - const effectivePathSegments = []; - let childNoteId = null; + const effectivePathSegments: string[] = []; + let childNoteId: string | null = null; let i = 0; while (true) { @@ -197,7 +197,7 @@ function getNotePath(node: Fancytree.FancytreeNode) { return ""; } - const path = []; + const path: string[] = []; while (node) { if (node.data.noteId) { @@ -236,7 +236,7 @@ async function getNoteTitle(noteId: string, parentNoteId: string | null = null) } async function getNotePathTitleComponents(notePath: string) { - const titleComponents = []; + const titleComponents: string[] = []; if (notePath.startsWith("root/")) { notePath = notePath.substr(5); diff --git a/apps/client/src/services/utils.ts b/apps/client/src/services/utils.ts index a52b7443a..ab6e45847 100644 --- a/apps/client/src/services/utils.ts +++ b/apps/client/src/services/utils.ts @@ -51,7 +51,7 @@ function getMonthsInDateRange(startDate: string, endDate: string) { const end = endDate.split("-"); const startYear = parseInt(start[0]); const endYear = parseInt(end[0]); - const dates = []; + const dates: string[] = []; for (let i = startYear; i <= endYear; i++) { const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1; @@ -84,7 +84,7 @@ function formatTimeInterval(ms: number) { const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); const plural = (count: number, name: string) => `${count} ${name}${count > 1 ? "s" : ""}`; - const segments = []; + const segments: string[] = []; if (days > 0) { segments.push(plural(days, "day")); @@ -149,7 +149,7 @@ function isMac() { export const hasTouchBar = (isMac() && isElectron()); -function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent) { +function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent | JQueryEventObject) { return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey); } diff --git a/apps/client/src/test/easy-froca.ts b/apps/client/src/test/easy-froca.ts index 0ccb768fd..045819ab5 100644 --- a/apps/client/src/test/easy-froca.ts +++ b/apps/client/src/test/easy-froca.ts @@ -28,7 +28,7 @@ interface NoteDefinition extends AttributeDefinitions, RelationDefinitions { * ]); */ export function buildNotes(notes: NoteDefinition[]) { - const ids = []; + const ids: string[] = []; for (const noteDef of notes) { ids.push(buildNote(noteDef).noteId); diff --git a/apps/client/src/widgets/attribute_widgets/attribute_detail.ts b/apps/client/src/widgets/attribute_widgets/attribute_detail.ts index 94d2bfa29..08bb764fc 100644 --- a/apps/client/src/widgets/attribute_widgets/attribute_detail.ts +++ b/apps/client/src/widgets/attribute_widgets/attribute_detail.ts @@ -718,7 +718,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget { } buildDefinitionValue() { - const props = []; + const props: string[] = []; if (this.$inputPromoted.is(":checked")) { props.push("promoted"); @@ -728,10 +728,10 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget { } } - props.push(this.$inputMultiplicity.val()); + props.push(this.$inputMultiplicity.val() as string); if (this.attrType === "label-definition") { - props.push(this.$inputLabelType.val()); + props.push(this.$inputLabelType.val() as string); if (this.$inputLabelType.val() === "number" && this.$inputNumberPrecision.val() !== "") { props.push(`precision=${this.$inputNumberPrecision.val()}`); diff --git a/apps/client/src/widgets/containers/launcher_container.ts b/apps/client/src/widgets/containers/launcher_container.ts index 5a814d1fa..f684d4e6b 100644 --- a/apps/client/src/widgets/containers/launcher_container.ts +++ b/apps/client/src/widgets/containers/launcher_container.ts @@ -29,7 +29,7 @@ export default class LauncherContainer extends FlexContainer { return; } - const newChildren = []; + const newChildren: LauncherWidget[] = []; for (const launcherNote of await visibleLaunchersRoot.getChildNotes()) { try { diff --git a/apps/client/src/widgets/containers/split_note_container.ts b/apps/client/src/widgets/containers/split_note_container.ts index 9bd5c04b5..99165437c 100644 --- a/apps/client/src/widgets/containers/split_note_container.ts +++ b/apps/client/src/widgets/containers/split_note_container.ts @@ -217,7 +217,7 @@ export default class SplitNoteContainer extends FlexContainer { } refreshNotShown(data: NoteSwitchedContext | EventData<"activeContextChanged">) { - const promises = []; + const promises: (Promise | null | undefined)[] = []; for (const subContext of data.noteContext.getMainContext().getSubContexts()) { if (!subContext.ntxId) { diff --git a/apps/client/src/widgets/find_in_text.ts b/apps/client/src/widgets/find_in_text.ts index 959fcdf34..e97a7a8df 100644 --- a/apps/client/src/widgets/find_in_text.ts +++ b/apps/client/src/widgets/find_in_text.ts @@ -2,16 +2,6 @@ import type { FindAndReplaceState, FindCommandResult } from "@triliumnext/ckedit import type { FindResult } from "./find.js"; import type FindWidget from "./find.js"; -// TODO: Deduplicate. -interface Match { - className: string; - clear(): void; - find(): { - from: number; - to: number; - }; -} - export default class FindInText { private parent: FindWidget; @@ -35,7 +25,7 @@ export default class FindInText { } const model = textEditor.model; - let findResult = null; + let findResult: FindCommandResult | null = null; let totalFound = 0; let currentFound = -1; diff --git a/apps/client/src/widgets/highlights_list.ts b/apps/client/src/widgets/highlights_list.ts index f37b82955..38a736f7e 100644 --- a/apps/client/src/widgets/highlights_list.ts +++ b/apps/client/src/widgets/highlights_list.ts @@ -243,7 +243,7 @@ export default class HighlightsListWidget extends RightPanelWidget { // Used to determine if a string is only a formula const onlyMathRegex = /^\\\([^\)]*?\)<\/span>(?:\\\([^\)]*?\)<\/span>)*$/; - for (let match = null, hltIndex = 0; (match = combinedRegex.exec(content)) !== null; hltIndex++) { + for (let match: RegExpMatchArray | null = null, hltIndex = 0; (match = combinedRegex.exec(content)) !== null; hltIndex++) { const subHtml = match[0]; const startIndex = match.index; const endIndex = combinedRegex.lastIndex; @@ -324,8 +324,9 @@ export default class HighlightsListWidget extends RightPanelWidget { }); } else { const textEditor = await this.noteContext.getTextEditor(); - if (textEditor) { - targetElement = $(textEditor.editing.view.domRoots.values().next().value) + const el = textEditor?.editing.view.domRoots.values().next().value; + if (el) { + targetElement = $(el) .find(findSubStr) .filter(function () { // When finding span[style*="color"] but not looking for span[style*="background-color"], diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 825e6d8f8..662562f42 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -350,7 +350,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { }, scrollParent: this.$tree, minExpandLevel: 2, // root can't be collapsed - click: (event: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent, data): boolean => { + click: (event, data): boolean => { this.activityDetected(); const targetType = data.targetType; @@ -745,7 +745,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { prepareChildren(parentNote: FNote) { utils.assertArguments(parentNote); - const noteList = []; + const noteList: Node[] = []; const hideArchivedNotes = this.hideArchivedNotes; @@ -839,7 +839,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { getExtraClasses(note: FNote) { utils.assertArguments(note); - const extraClasses = []; + const extraClasses: string[] = []; if (note.isProtected) { extraClasses.push("protected"); @@ -1265,8 +1265,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const allBranchesDeleted = branchRows.every((branchRow) => !!branchRow.isDeleted); // activeNode is supposed to be moved when we find out activeNode is deleted but not all branches are deleted. save it for fixing activeNodePath after all nodes loaded. - let movedActiveNode = null; - let parentsOfAddedNodes = []; + let movedActiveNode: Fancytree.FancytreeNode | null = null; + let parentsOfAddedNodes: Fancytree.FancytreeNode[] = []; for (const branchRow of branchRows) { if (branchRow.noteId) { diff --git a/apps/client/src/widgets/ribbon_widgets/note_paths.ts b/apps/client/src/widgets/ribbon_widgets/note_paths.ts index 131fed2a4..8681bfd21 100644 --- a/apps/client/src/widgets/ribbon_widgets/note_paths.ts +++ b/apps/client/src/widgets/ribbon_widgets/note_paths.ts @@ -85,7 +85,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget { this.$notePathIntro.text(t("note_paths.intro_not_placed")); } - const renderedPaths = []; + const renderedPaths: JQuery[] = []; for (const notePathRecord of sortedNotePaths) { const notePath = notePathRecord.notePath; @@ -100,7 +100,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget { const $pathItem = $("
  • "); const pathSegments: string[] = []; const lastIndex = notePath.length - 1; - + for (let i = 0; i < notePath.length; i++) { const noteId = notePath[i]; pathSegments.push(noteId); @@ -109,13 +109,13 @@ export default class NotePathsWidget extends NoteContextAwareWidget { $noteLink.find("a").addClass("no-tooltip-preview tn-link"); $pathItem.append($noteLink); - + if (i != lastIndex) { $pathItem.append(" / "); } } - const icons = []; + const icons: string[] = []; if (this.notePath === notePath.join("/")) { $pathItem.addClass("path-current"); diff --git a/apps/client/src/widgets/ribbon_widgets/promoted_attributes.ts b/apps/client/src/widgets/ribbon_widgets/promoted_attributes.ts index 861fef657..d94cfdbd1 100644 --- a/apps/client/src/widgets/ribbon_widgets/promoted_attributes.ts +++ b/apps/client/src/widgets/ribbon_widgets/promoted_attributes.ts @@ -122,7 +122,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { return; } - const $cells = []; + const $cells: JQuery[] = []; for (const definitionAttr of promotedDefAttrs) { const valueType = definitionAttr.name.startsWith("label:") ? "label" : "relation"; diff --git a/apps/client/src/widgets/tab_row.ts b/apps/client/src/widgets/tab_row.ts index 83999d876..92b81f6dd 100644 --- a/apps/client/src/widgets/tab_row.ts +++ b/apps/client/src/widgets/tab_row.ts @@ -208,7 +208,7 @@ const TAB_ROW_TPL = ` color: var(--active-tab-text-color); box-shadow: inset -1px 0 0 0 var(--main-border-color); } - + .tab-scroll-button-right { color: var(--active-tab-text-color); box-shadow: inset 1px 0 0 0 var(--main-border-color); @@ -279,7 +279,7 @@ const TAB_ROW_TPL = ` width: 100%; height: 100%; } - + .tab-row-widget-scrolling-container { overflow-x: auto; overflow-y: hidden; @@ -287,9 +287,9 @@ const TAB_ROW_TPL = ` } /* Chrome/Safari */ .tab-row-widget-scrolling-container::-webkit-scrollbar { - display: none; + display: none; } - +
    diff --git a/apps/client/src/widgets/toc.ts b/apps/client/src/widgets/toc.ts index c5fda409c..a5db7ff1b 100644 --- a/apps/client/src/widgets/toc.ts +++ b/apps/client/src/widgets/toc.ts @@ -64,7 +64,7 @@ const TPL = /*html*/`
    } .toc > ol { - --toc-depth-level: 1; + --toc-depth-level: 1; } .toc > ol > ol { --toc-depth-level: 2; @@ -84,7 +84,7 @@ const TPL = /*html*/`
    } .toc li { - padding-left: calc((var(--toc-depth-level) - 1) * 20px + 4px); + padding-left: calc((var(--toc-depth-level) - 1) * 20px + 4px); } .toc li .collapse-button { @@ -304,7 +304,7 @@ export default class TocWidget extends RightPanelWidget { const validHeadingKeys = new Set(); // Used to clean up obsolete entries in tocCollapsedHeadings let headingCount = 0; - for (let m = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) { + for (let m: RegExpMatchArray | null = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) { // // Nest/unnest whatever necessary number of ordered lists // @@ -394,12 +394,14 @@ export default class TocWidget extends RightPanelWidget { const isDocNote = this.note.type === "doc"; const isReadOnly = await this.noteContext.isReadOnly(); - let $container; + let $container: JQuery | null = null; if (isReadOnly || isDocNote) { $container = await this.noteContext.getContentElement(); } else { const textEditor = await this.noteContext.getTextEditor(); - $container = $(textEditor.sourceElement); + if (textEditor?.sourceElement) { + $container = $(textEditor.sourceElement); + } } const headingElement = $container?.find(":header:not(section.include-note :header)")?.[headingIndex]; diff --git a/apps/client/src/widgets/type_widgets/abstract_svg_split_type_widget.ts b/apps/client/src/widgets/type_widgets/abstract_svg_split_type_widget.ts index 24c2c1ae4..45eba8890 100644 --- a/apps/client/src/widgets/type_widgets/abstract_svg_split_type_widget.ts +++ b/apps/client/src/widgets/type_widgets/abstract_svg_split_type_widget.ts @@ -138,8 +138,8 @@ export default abstract class AbstractSvgSplitTypeWidget extends AbstractSplitTy */ async #setupPanZoom(preservePanZoom: boolean) { // Clean up - let pan = null; - let zoom = null; + let pan: SvgPanZoom.Point | null = null; + let zoom: number | null = null; if (preservePanZoom && this.zoomInstance) { // Store pan and zoom for same note, when the user is editing the note. pan = this.zoomInstance.getPan(); diff --git a/apps/client/src/widgets/type_widgets/ckeditor/config.spec.ts b/apps/client/src/widgets/type_widgets/ckeditor/config.spec.ts index 8c96629b9..3bce30d9b 100644 --- a/apps/client/src/widgets/type_widgets/ckeditor/config.spec.ts +++ b/apps/client/src/widgets/type_widgets/ckeditor/config.spec.ts @@ -6,7 +6,7 @@ type ToolbarConfig = string | "|" | { items: ToolbarConfig[] }; describe("CKEditor config", () => { it("has same toolbar items for fixed and floating", () => { function traverseItems(config: ToolbarConfig): string[] { - const result = []; + const result: (string | string[])[] = []; if (typeof config === "object") { for (const item of config.items) { result.push(traverseItems(item)); diff --git a/apps/client/src/widgets/type_widgets/ckeditor/config.ts b/apps/client/src/widgets/type_widgets/ckeditor/config.ts index 8cd2ba66d..5a3262a1c 100644 --- a/apps/client/src/widgets/type_widgets/ckeditor/config.ts +++ b/apps/client/src/widgets/type_widgets/ckeditor/config.ts @@ -111,7 +111,7 @@ export function buildConfig(): EditorConfig { }, mapLanguageName: getHighlightJsNameForMime, defaultMimeType: MIME_TYPE_AUTO, - enabled: isSyntaxHighlightEnabled + enabled: isSyntaxHighlightEnabled() }, clipboard: { copy: copyText @@ -134,7 +134,7 @@ export function buildToolbarConfig(isClassicToolbar: boolean) { export function buildMobileToolbar() { const classicConfig = buildClassicToolbar(false); - const items = []; + const items: string[] = []; for (const item of classicConfig.toolbar.items) { if (typeof item === "object" && "items" in item) { diff --git a/apps/client/src/widgets/type_widgets/editable_text.ts b/apps/client/src/widgets/type_widgets/editable_text.ts index a50b7a638..ca4ea98b2 100644 --- a/apps/client/src/widgets/type_widgets/editable_text.ts +++ b/apps/client/src/widgets/type_widgets/editable_text.ts @@ -589,7 +589,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command)?.value as boolean) }); - let headingSelectedIndex = undefined; + let headingSelectedIndex: number | undefined = undefined; const headingCommand = editor.commands.get("heading"); const paragraphCommand = editor.commands.get("paragraph"); if (paragraphCommand?.value) { diff --git a/apps/client/src/widgets/type_widgets/linters/mermaid.spec.ts b/apps/client/src/widgets/type_widgets/linters/mermaid.spec.ts deleted file mode 100644 index 638892d71..000000000 --- a/apps/client/src/widgets/type_widgets/linters/mermaid.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { trimIndentation } from "@triliumnext/commons"; -import { validateMermaid } from "./mermaid.js"; - -describe("Mermaid linter", () => { - - (global as any).CodeMirror = { - Pos(line: number, col: number) { - return { line, col }; - } - }; - - it("reports correctly bad diagram type", async () => { - const input = trimIndentation`\ - stateDiagram-v23 - [*] -> Still - `; - - const result = await validateMermaid(input); - expect(result).toMatchObject([{ - message: "Expecting 'SPACE', 'NL', 'SD', got 'ID'", - from: { line: 0, col: 0 }, - to: { line: 0, col: 1 } - }]); - }); - - it("reports correctly basic arrow missing in diagram", async () => { - const input = trimIndentation`\ - xychart-beta horizontal - title "Percentage usge" - x-axis [data, sys, usr, var] - y-axis 0--->100 - bar [20, 70, 0, 0] - `; - - const result = await validateMermaid(input); - expect(result).toMatchObject([{ - message: "Expecting 'ARROW_DELIMITER', got 'MINUS'", - from: { line: 3, col: 8 }, - to: { line: 3, col: 9 } - }]); - }); -}); diff --git a/apps/client/src/widgets/type_widgets/linters/mermaid.ts b/apps/client/src/widgets/type_widgets/linters/mermaid.ts deleted file mode 100644 index e0773e652..000000000 --- a/apps/client/src/widgets/type_widgets/linters/mermaid.ts +++ /dev/null @@ -1,58 +0,0 @@ -import mermaid from "mermaid"; - -interface MermaidParseError extends Error { - hash: { - text: string; - token: string; - line: number; - loc: { - first_line: number; - first_column: number; - last_line: number; - last_column: number; - }; - expected: string[] - } -} - -export default function registerErrorReporter() { - CodeMirror.registerHelper("lint", null, validateMermaid); -} - -export async function validateMermaid(text: string) { - if (!text.trim()) { - return []; - } - - try { - await mermaid.parse(text); - } catch (e: unknown) { - console.warn("Got validation error", JSON.stringify(e)); - - const mermaidError = (e as MermaidParseError); - const loc = mermaidError.hash.loc; - - let firstCol = loc.first_column + 1; - let lastCol = loc.last_column + 1; - - if (firstCol === 1 && lastCol === 1) { - firstCol = 0; - } - - let messageLines = mermaidError.message.split("\n"); - if (messageLines.length >= 4) { - messageLines = messageLines.slice(3); - } - - return [ - { - message: messageLines.join("\n"), - severity: "error", - from: CodeMirror.Pos(loc.first_line - 1, firstCol), - to: CodeMirror.Pos(loc.last_line - 1, lastCol) - } - ]; - } - - return []; -} diff --git a/apps/client/src/widgets/type_widgets/mind_map.ts b/apps/client/src/widgets/type_widgets/mind_map.ts index 83d2e0e96..18867bc83 100644 --- a/apps/client/src/widgets/type_widgets/mind_map.ts +++ b/apps/client/src/widgets/type_widgets/mind_map.ts @@ -207,8 +207,8 @@ export default class MindMapWidget extends TypeWidget { await this.#initLibrary(content?.direction); } - this.mind.refresh(content ?? this.MindElixir.new(NEW_TOPIC_NAME)); - this.mind.toCenter(); + this.mind!.refresh(content ?? this.MindElixir.new(NEW_TOPIC_NAME)); + this.mind!.toCenter(); } async #initLibrary(direction?: number) { @@ -259,7 +259,7 @@ export default class MindMapWidget extends TypeWidget { } async renderSvg() { - return await this.mind.exportSvg().text(); + return await this.mind!.exportSvg().text(); } async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { diff --git a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts index 8f76eac4b..da7321d18 100644 --- a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts +++ b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts @@ -198,7 +198,7 @@ export default class AiSettingsWidget extends OptionsWidget { const providerPrecedence = (this.$widget.find('.ai-provider-precedence').val() as string || '').split(','); // Check for OpenAI configuration if it's in the precedence list - const openaiWarnings = []; + const openaiWarnings: string[] = []; if (providerPrecedence.includes('openai')) { const openaiApiKey = this.$widget.find('.openai-api-key').val(); if (!openaiApiKey) { @@ -207,7 +207,7 @@ export default class AiSettingsWidget extends OptionsWidget { } // Check for Anthropic configuration if it's in the precedence list - const anthropicWarnings = []; + const anthropicWarnings: string[] = []; if (providerPrecedence.includes('anthropic')) { const anthropicApiKey = this.$widget.find('.anthropic-api-key').val(); if (!anthropicApiKey) { @@ -216,7 +216,7 @@ export default class AiSettingsWidget extends OptionsWidget { } // Check for Voyage configuration if it's in the precedence list - const voyageWarnings = []; + const voyageWarnings: string[] = []; if (providerPrecedence.includes('voyage')) { const voyageApiKey = this.$widget.find('.voyage-api-key').val(); if (!voyageApiKey) { @@ -225,7 +225,7 @@ export default class AiSettingsWidget extends OptionsWidget { } // Check for Ollama configuration if it's in the precedence list - const ollamaWarnings = []; + const ollamaWarnings: string[] = []; if (providerPrecedence.includes('ollama')) { const ollamaBaseUrl = this.$widget.find('.ollama-base-url').val(); if (!ollamaBaseUrl) { @@ -234,7 +234,7 @@ export default class AiSettingsWidget extends OptionsWidget { } // Similar checks for embeddings - const embeddingWarnings = []; + const embeddingWarnings: string[] = []; const embeddingsEnabled = this.$widget.find('.enable-automatic-indexing').prop('checked'); if (embeddingsEnabled) { diff --git a/apps/client/src/widgets/view_widgets/calendar_view.ts b/apps/client/src/widgets/view_widgets/calendar_view.ts index fdc693dc1..d6eafa791 100644 --- a/apps/client/src/widgets/view_widgets/calendar_view.ts +++ b/apps/client/src/widgets/view_widgets/calendar_view.ts @@ -83,6 +83,13 @@ interface CreateChildResponse { }; } +interface Event { + startDate: string, + endDate?: string | null, + startTime?: string | null, + endTime?: string | null +} + const CALENDAR_VIEWS = [ "timeGridWeek", "dayGridMonth", @@ -325,8 +332,8 @@ export default class CalendarView extends ViewMode { } #parseStartEndTimeFromEvent(e: DateSelectArg | EventImpl) { - let startTime = null; - let endTime = null; + let startTime: string | undefined | null = null; + let endTime: string | undefined | null = null; if (!e.allDay) { startTime = CalendarView.#formatTimeToLocalISO(e.start); endTime = CalendarView.#formatTimeToLocalISO(e.end); @@ -391,7 +398,7 @@ export default class CalendarView extends ViewMode { } async #buildEventsForCalendar(e: EventSourceFuncArg) { - const events = []; + const events: EventInput[] = []; // Gather all the required date note IDs. const dateRange = utils.getMonthsInDateRange(e.startStr, e.endStr); @@ -483,12 +490,7 @@ export default class CalendarView extends ViewMode { return note.getLabelValue(defaultLabelName); } - static async buildEvent(note: FNote, { startDate, endDate, startTime, endTime }: { - startDate: string, - endDate?: string | null, - startTime?: string | null, - endTime?: string | null - }) { + static async buildEvent(note: FNote, { startDate, endDate, startTime, endTime }: Event) { const customTitleAttributeName = note.getLabelValue("calendar:title"); const titles = await CalendarView.#parseCustomTitle(customTitleAttributeName, note); const color = note.getLabelValue("calendar:color") ?? note.getLabelValue("color"); @@ -553,7 +555,7 @@ export default class CalendarView extends ViewMode { if (relations.length > 0) { const noteIds = relations.map((r) => r.targetNoteId); const notesFromRelation = await froca.getNotes(noteIds); - const titles = []; + const titles: string[][] = []; for (const targetNote of notesFromRelation) { const targetCustomTitleValue = targetNote.getAttributeValue("label", "calendar:title"); @@ -631,7 +633,7 @@ export default class CalendarView extends ViewMode { // Icon button. const iconEl = subItem.querySelector("span.fc-icon"); - let icon = null; + let icon: string | null = null; if (iconEl?.classList.contains("fc-icon-chevron-left")) { icon = "NSImageNameTouchBarGoBackTemplate"; mode = "buttons"; diff --git a/apps/db-compare/tsconfig.app.json b/apps/db-compare/tsconfig.app.json index c5944cc79..bdf9ba861 100644 --- a/apps/db-compare/tsconfig.app.json +++ b/apps/db-compare/tsconfig.app.json @@ -6,8 +6,7 @@ "node" ], "rootDir": "src", - "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo", - "verbatimModuleSyntax": false + "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo" }, "include": [ "src/**/*.ts" diff --git a/apps/desktop/tsconfig.app.json b/apps/desktop/tsconfig.app.json index 1c37e3a04..24b95c5ed 100644 --- a/apps/desktop/tsconfig.app.json +++ b/apps/desktop/tsconfig.app.json @@ -5,7 +5,6 @@ "moduleResolution": "bundler", "target": "ES2020", "outDir": "dist", - "strict": false, "types": [ "node", "express" diff --git a/apps/server-e2e/tsconfig.json b/apps/server-e2e/tsconfig.json index 5e023db98..1df867f3a 100644 --- a/apps/server-e2e/tsconfig.json +++ b/apps/server-e2e/tsconfig.json @@ -3,8 +3,7 @@ "compilerOptions": { "allowJs": true, "outDir": "out-tsc/playwright", - "sourceMap": false, - "verbatimModuleSyntax": false + "sourceMap": false }, "include": [ "**/*.ts", diff --git a/apps/server/src/becca/becca-interface.ts b/apps/server/src/becca/becca-interface.ts index 7620ef3f5..005a5cc52 100644 --- a/apps/server/src/becca/becca-interface.ts +++ b/apps/server/src/becca/becca-interface.ts @@ -251,7 +251,7 @@ export default class Becca { getAllNoteSet() { // caching this since it takes 10s of milliseconds to fill this initial NoteSet for many notes if (!this.allNoteSetCache) { - const allNotes = []; + const allNotes: BNote[] = []; for (const noteId in this.notes) { const note = this.notes[noteId]; diff --git a/apps/server/src/becca/becca_service.ts b/apps/server/src/becca/becca_service.ts index 8f54728ca..92967da34 100644 --- a/apps/server/src/becca/becca_service.ts +++ b/apps/server/src/becca/becca_service.ts @@ -76,7 +76,7 @@ function getNoteTitleArrayForPath(notePathArray: string[]) { return [getNoteTitle(notePathArray[0])]; } - const titles = []; + const titles: string[] = []; let parentNoteId = "root"; let hoistedNotePassed = false; diff --git a/apps/server/src/becca/entities/bnote.ts b/apps/server/src/becca/entities/bnote.ts index 3813b5bbc..570d1b7b7 100644 --- a/apps/server/src/becca/entities/bnote.ts +++ b/apps/server/src/becca/entities/bnote.ts @@ -388,7 +388,7 @@ class BNote extends AbstractBeccaEntity { } } - const templateAttributes = []; + const templateAttributes: BAttribute[] = []; for (const ownedAttr of parentAttributes) { // parentAttributes so we process also inherited templates diff --git a/apps/server/src/becca/similarity.ts b/apps/server/src/becca/similarity.ts index 8f4bdf0ff..fe7ecda21 100644 --- a/apps/server/src/becca/similarity.ts +++ b/apps/server/src/becca/similarity.ts @@ -254,7 +254,7 @@ function hasConnectingRelation(sourceNote: BNote, targetNote: BNote) { } async function findSimilarNotes(noteId: string): Promise { - const results = []; + const results: SimilarNote[] = []; let i = 0; const baseNote = becca.notes[noteId]; diff --git a/apps/server/src/routes/api/note_map.ts b/apps/server/src/routes/api/note_map.ts index f621c7b2f..470152bd6 100644 --- a/apps/server/src/routes/api/note_map.ts +++ b/apps/server/src/routes/api/note_map.ts @@ -12,6 +12,11 @@ interface Backlink { excerpts?: string[]; } +interface TreeLink { + sourceNoteId: string; + targetNoteId: string; +} + function buildDescendantCountMap(noteIdsToCount: string[]) { if (!Array.isArray(noteIdsToCount)) { throw new Error("noteIdsToCount: type error"); @@ -50,7 +55,7 @@ function getNeighbors(note: BNote, depth: number): string[] { return []; } - const retNoteIds = []; + const retNoteIds: string[] = []; function isIgnoredRelation(relation: BAttribute) { return ["relationMapLink", "template", "inherit", "image", "ancestor"].includes(relation.name); @@ -196,7 +201,7 @@ function getTreeMap(req: Request) { const noteIds = new Set(); notes.forEach(([noteId]) => noteId && noteIds.add(noteId)); - const links = []; + const links: TreeLink[] = []; for (const { parentNoteId, childNoteId } of subtree.relationships) { if (!noteIds.has(parentNoteId) || !noteIds.has(childNoteId)) { @@ -246,7 +251,7 @@ function findExcerpts(sourceNote: BNote, referencedNoteId: string) { const html = sourceNote.getContent(); const document = new JSDOM(html).window.document; - const excerpts = []; + const excerpts: string[] = []; removeImages(document); diff --git a/apps/server/src/routes/api/options.ts b/apps/server/src/routes/api/options.ts index 3a0a9b06b..c69f7568f 100644 --- a/apps/server/src/routes/api/options.ts +++ b/apps/server/src/routes/api/options.ts @@ -9,6 +9,12 @@ import { changeLanguage, getLocales } from "../../services/i18n.js"; import type { OptionNames } from "@triliumnext/commons"; import config from "../../services/config.js"; +interface UserTheme { + val: string; // value of the theme, used in the URL + title: string; // title of the theme, displayed in the UI + noteId: string; // ID of the note containing the theme +} + // options allowed to be updated directly in the Options dialog const ALLOWED_OPTIONS = new Set([ "eraseEntitiesAfterTimeInSeconds", @@ -177,7 +183,7 @@ function update(name: string, value: string) { function getUserThemes() { const notes = searchService.searchNotes("#appTheme", { ignoreHoistedNote: true }); - const ret = []; + const ret: UserTheme[] = []; for (const note of notes) { let value = note.getOwnedLabelValue("appTheme"); diff --git a/apps/server/src/routes/api/recent_changes.ts b/apps/server/src/routes/api/recent_changes.ts index 67b7436a0..1e539710e 100644 --- a/apps/server/src/routes/api/recent_changes.ts +++ b/apps/server/src/routes/api/recent_changes.ts @@ -21,7 +21,7 @@ interface RecentChangeRow { function getRecentChanges(req: Request) { const { ancestorNoteId } = req.params; - let recentChanges = []; + let recentChanges: RecentChangeRow[] = []; const revisionRows = sql.getRows(` SELECT diff --git a/apps/server/src/routes/api/script.ts b/apps/server/src/routes/api/script.ts index c702a82d8..d9d2fac8f 100644 --- a/apps/server/src/routes/api/script.ts +++ b/apps/server/src/routes/api/script.ts @@ -1,6 +1,6 @@ "use strict"; -import scriptService from "../../services/script.js"; +import scriptService, { type Bundle } from "../../services/script.js"; import attributeService from "../../services/attributes.js"; import becca from "../../becca/becca.js"; import syncService from "../../services/sync.js"; @@ -54,7 +54,7 @@ function run(req: Request) { function getBundlesWithLabel(label: string, value?: string) { const notes = attributeService.getNotesWithLabel(label, value); - const bundles = []; + const bundles: Bundle[] = []; for (const note of notes) { const bundle = scriptService.getScriptBundleForFrontend(note); @@ -97,7 +97,7 @@ function getRelationBundles(req: Request) { const targetNoteIds = filtered.map((relation) => relation.value); const uniqueNoteIds = Array.from(new Set(targetNoteIds)); - const bundles = []; + const bundles: Bundle[] = []; for (const noteId of uniqueNoteIds) { const note = becca.getNoteOrThrow(noteId); diff --git a/apps/server/src/routes/api/sql.ts b/apps/server/src/routes/api/sql.ts index ce2fc5127..0a1fbb28d 100644 --- a/apps/server/src/routes/api/sql.ts +++ b/apps/server/src/routes/api/sql.ts @@ -6,9 +6,14 @@ import type { Request } from "express"; import ValidationError from "../../errors/validation_error.js"; import { safeExtractMessageAndStackFromError } from "../../services/utils.js"; +interface Table { + name: string; + columns: unknown[]; +} + function getSchema() { - const tableNames = sql.getColumn(/*sql*/`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); - const tables = []; + const tableNames = sql.getColumn(/*sql*/`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); + const tables: Table[] = []; for (const tableName of tableNames) { tables.push({ @@ -31,7 +36,7 @@ function execute(req: Request) { const queries = content.split("\n---"); try { - const results = []; + const results: unknown[] = []; for (let query of queries) { query = query.trim(); diff --git a/apps/server/src/routes/api/tree.ts b/apps/server/src/routes/api/tree.ts index 885529c93..bd6c9bb61 100644 --- a/apps/server/src/routes/api/tree.ts +++ b/apps/server/src/routes/api/tree.ts @@ -5,6 +5,7 @@ import log from "../../services/log.js"; import NotFoundError from "../../errors/not_found_error.js"; import type { Request } from "express"; import type BNote from "../../becca/entities/bnote.js"; +import type { AttributeRow, BranchRow, NoteRow } from "@triliumnext/commons"; function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set) { const noteIds = new Set(_noteIds); @@ -53,7 +54,7 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set) { collectEntityIds(note); } - const notes = []; + const notes: NoteRow[] = []; for (const noteId of collectedNoteIds) { const note = becca.notes[noteId]; @@ -68,7 +69,7 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set) { }); } - const branches = []; + const branches: BranchRow[] = []; if (noteIds.has("root")) { branches.push({ @@ -99,7 +100,7 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set) { }); } - const attributes = []; + const attributes: AttributeRow[] = []; for (const attributeId of collectedAttributeIds) { const attribute = becca.attributes[attributeId]; diff --git a/apps/server/src/services/content_hash.ts b/apps/server/src/services/content_hash.ts index 0247bd335..6c8365e2b 100644 --- a/apps/server/src/services/content_hash.ts +++ b/apps/server/src/services/content_hash.ts @@ -7,6 +7,11 @@ import eraseService from "./erase.js"; type SectorHash = Record; +interface FailedCheck { + entityName: string; + sector: string[1]; +} + function getEntityHashes() { // blob erasure is not synced, we should check before each sync if there's some blob to erase eraseService.eraseUnusedBlobs(); @@ -56,7 +61,7 @@ function getEntityHashes() { function checkContentHashes(otherHashes: Record) { const entityHashes = getEntityHashes(); - const failedChecks = []; + const failedChecks: FailedCheck[] = []; for (const entityName in entityHashes) { const thisSectorHashes: SectorHash = entityHashes[entityName] || {}; diff --git a/apps/server/src/services/i18n.ts b/apps/server/src/services/i18n.ts index 3bdf6f9cd..3e1f4d1a6 100644 --- a/apps/server/src/services/i18n.ts +++ b/apps/server/src/services/i18n.ts @@ -51,7 +51,7 @@ export function getLocales(): Locale[] { } function getCurrentLanguage(): LOCALE_IDS { - let language: string; + let language: string | null = null; if (sql_init.isDbInitialized()) { language = options.getOptionOrNull("locale"); } diff --git a/apps/server/src/services/import/opml.ts b/apps/server/src/services/import/opml.ts index cdc01011f..934578c70 100644 --- a/apps/server/src/services/import/opml.ts +++ b/apps/server/src/services/import/opml.ts @@ -84,7 +84,7 @@ async function importOpml(taskContext: TaskContext, fileBuffer: string | Buffer, } const outlines = xml.opml.body[0].outline || []; - let returnNote = null; + let returnNote: BNote | null = null; for (const outline of outlines) { const note = importOutline(outline, parentNote.noteId); diff --git a/apps/server/src/services/llm/ai_interface.ts b/apps/server/src/services/llm/ai_interface.ts index b8349b5b2..df8cc6914 100644 --- a/apps/server/src/services/llm/ai_interface.ts +++ b/apps/server/src/services/llm/ai_interface.ts @@ -9,7 +9,7 @@ export interface Message { content: string; name?: string; tool_call_id?: string; - tool_calls?: ToolCall[]; + tool_calls?: ToolCall[] | null; sessionId?: string; // Optional session ID for WebSocket communication } @@ -210,7 +210,7 @@ export interface ChatResponse { stream?: (callback: (chunk: StreamChunk) => Promise | void) => Promise; /** Tool calls from the LLM (if tools were used and the model supports them) */ - tool_calls?: ToolCall[]; + tool_calls?: ToolCall[] | null; } export interface AIService { diff --git a/apps/server/src/services/llm/context/code_handlers.ts b/apps/server/src/services/llm/context/code_handlers.ts index f4b1fca97..6a7d0f7af 100644 --- a/apps/server/src/services/llm/context/code_handlers.ts +++ b/apps/server/src/services/llm/context/code_handlers.ts @@ -160,7 +160,7 @@ function extractJsStructure(content: string): string { } // Look for class declarations - const classes = []; + const classes: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('class ') || line.includes(' class ')) { @@ -173,7 +173,7 @@ function extractJsStructure(content: string): string { } // Look for function declarations - const functions = []; + const functions: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('function ') || @@ -212,7 +212,7 @@ function extractPythonStructure(content: string): string { } // Look for class declarations - const classes = []; + const classes: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('class ')) { @@ -225,7 +225,7 @@ function extractPythonStructure(content: string): string { } // Look for function declarations - const functions = []; + const functions: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('def ')) { @@ -263,7 +263,7 @@ function extractClassBasedStructure(content: string): string { } // Look for class declarations - const classes = []; + const classes: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.match(/^(public|private|protected)?\s*(class|interface|enum)\s+\w+/)) { @@ -276,7 +276,7 @@ function extractClassBasedStructure(content: string): string { } // Look for method declarations - const methods = []; + const methods: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.match(/^(public|private|protected)?\s*(static)?\s*[\w<>[\]]+\s+\w+\s*\(/)) { @@ -319,7 +319,7 @@ function extractGoStructure(content: string): string { } // Look for type declarations (structs, interfaces) - const types = []; + const types: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('type ') && (line.includes(' struct ') || line.includes(' interface '))) { @@ -332,7 +332,7 @@ function extractGoStructure(content: string): string { } // Look for function declarations - const functions = []; + const functions: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('func ')) { @@ -366,7 +366,7 @@ function extractRustStructure(content: string): string { } // Look for struct/enum/trait declarations - const types = []; + const types: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('struct ') || line.startsWith('enum ') || line.startsWith('trait ')) { @@ -379,8 +379,8 @@ function extractRustStructure(content: string): string { } // Look for function/impl declarations - const functions = []; - const impls = []; + const functions: string[] = []; + const impls: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); diff --git a/apps/server/src/services/llm/context/content_chunking.ts b/apps/server/src/services/llm/context/content_chunking.ts index a9f032684..0e57da515 100644 --- a/apps/server/src/services/llm/context/content_chunking.ts +++ b/apps/server/src/services/llm/context/content_chunking.ts @@ -198,7 +198,7 @@ export async function semanticChunking( // Try to split on headers first const headerPattern = /#{1,6}\s+.+|]*>.*?<\/h[1-6]>/g; - const sections = []; + const sections: string[] = []; let lastIndex = 0; let match; diff --git a/apps/server/src/services/llm/context_extractors/query_decomposition_tool.ts b/apps/server/src/services/llm/context_extractors/query_decomposition_tool.ts index 2e65ada76..917403abb 100644 --- a/apps/server/src/services/llm/context_extractors/query_decomposition_tool.ts +++ b/apps/server/src/services/llm/context_extractors/query_decomposition_tool.ts @@ -439,7 +439,7 @@ export class QueryDecompositionTool { // If no pattern match, try to extract noun phrases const words = query.split(/\s+/); - const potentialEntities = []; + const potentialEntities: string[] = []; let currentPhrase = ''; for (const word of words) { diff --git a/apps/server/src/services/llm/embeddings/base_embeddings.ts b/apps/server/src/services/llm/embeddings/base_embeddings.ts index ffc57174c..0332bbf51 100644 --- a/apps/server/src/services/llm/embeddings/base_embeddings.ts +++ b/apps/server/src/services/llm/embeddings/base_embeddings.ts @@ -246,7 +246,7 @@ export abstract class BaseEmbeddingProvider { */ protected generateNoteContextText(context: NoteEmbeddingContext): string { // Build a relationship-focused summary first - const relationshipSummary = []; + const relationshipSummary: string[] = []; // Summarize the note's place in the hierarchy if (context.parentTitles.length > 0) { diff --git a/apps/server/src/services/llm/embeddings/embeddings_interface.ts b/apps/server/src/services/llm/embeddings/embeddings_interface.ts index b1fa333bf..1db6c5099 100644 --- a/apps/server/src/services/llm/embeddings/embeddings_interface.ts +++ b/apps/server/src/services/llm/embeddings/embeddings_interface.ts @@ -22,20 +22,24 @@ export interface NoteEmbeddingContext { title: string; mime: string; }[]; - backlinks?: { - sourceNoteId: string; - sourceTitle: string; - relationName: string; - }[]; - relatedNotes?: { - targetNoteId: string; - targetTitle: string; - relationName: string; - }[]; + backlinks?: Backlink[]; + relatedNotes?: RelatedNote[]; labelValues?: Record; templateTitles?: string[]; } +export interface Backlink { + sourceNoteId: string; + sourceTitle: string; + relationName: string; +} + +export interface RelatedNote { + targetNoteId: string; + targetTitle: string; + relationName: string; +} + /** * Information about an embedding model's capabilities */ diff --git a/apps/server/src/services/llm/embeddings/queue.ts b/apps/server/src/services/llm/embeddings/queue.ts index 1b27559d1..6b3b5e415 100644 --- a/apps/server/src/services/llm/embeddings/queue.ts +++ b/apps/server/src/services/llm/embeddings/queue.ts @@ -13,6 +13,21 @@ import indexService from '../index_service.js'; // Track which notes are currently being processed const notesInProcess = new Set(); +interface FailedItemRow { + noteId: string; + operation: string; + attempts: number; + lastAttempt: string; + error: string | null; + failed: number; +} + +interface FailedItemWithTitle extends FailedItemRow { + title?: string; + failureType: 'chunks' | 'full'; + isPermanent: boolean; +} + /** * Queues a note for embedding update */ @@ -77,17 +92,17 @@ export async function queueNoteForEmbedding(noteId: string, operation = 'UPDATE' */ export async function getFailedEmbeddingNotes(limit: number = 100): Promise { // Get notes with failed embedding attempts or permanently failed flag - const failedQueueItems = await sql.getRows(` + const failedQueueItems = sql.getRows(` SELECT noteId, operation, attempts, lastAttempt, error, failed FROM embedding_queue WHERE attempts > 0 OR failed = 1 ORDER BY failed DESC, attempts DESC, lastAttempt DESC LIMIT ?`, [limit] - ) as {noteId: string, operation: string, attempts: number, lastAttempt: string, error: string, failed: number}[]; + ); // Add titles to the failed notes - const failedNotesWithTitles = []; + const failedNotesWithTitles: FailedItemWithTitle[] = []; for (const item of failedQueueItems) { const note = becca.getNote(item.noteId); if (note) { diff --git a/apps/server/src/services/llm/embeddings/storage.ts b/apps/server/src/services/llm/embeddings/storage.ts index 67063e63b..675047a76 100644 --- a/apps/server/src/services/llm/embeddings/storage.ts +++ b/apps/server/src/services/llm/embeddings/storage.ts @@ -8,6 +8,14 @@ import entityChangesService from "../../../services/entity_changes.js"; import type { EntityChange } from "../../../services/entity_changes_interface.js"; import { EMBEDDING_CONSTANTS } from "../constants/embedding_constants.js"; import { SEARCH_CONSTANTS } from '../constants/search_constants.js'; + +interface Similarity { + noteId: string; + similarity: number; + contentType: string; + bonuses?: Record; // Optional for debugging +} + /** * Creates or updates an embedding for a note */ @@ -434,7 +442,7 @@ async function processEmbeddings(queryEmbedding: Float32Array, embeddings: any[] return { bonuses, totalBonus }; } - const similarities = []; + const similarities: Similarity[] = []; try { // Try to extract the original query text if it was added to the metadata diff --git a/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts b/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts index 35634c210..e5406997d 100644 --- a/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts +++ b/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts @@ -95,7 +95,7 @@ export class ModelSelectionStage extends BasePipelineStage p.trim()); } else if (providerPrecedence.startsWith('[') && providerPrecedence.endsWith(']')) { diff --git a/apps/server/src/services/llm/providers/anthropic_service.ts b/apps/server/src/services/llm/providers/anthropic_service.ts index 955ef494a..cbdbb6814 100644 --- a/apps/server/src/services/llm/providers/anthropic_service.ts +++ b/apps/server/src/services/llm/providers/anthropic_service.ts @@ -7,6 +7,21 @@ import { getAnthropicOptions } from './providers.js'; import log from '../../log.js'; import Anthropic from '@anthropic-ai/sdk'; import { SEARCH_CONSTANTS } from '../constants/search_constants.js'; +import type { ToolCall } from '../tools/tool_interfaces.js'; + +interface AnthropicMessage extends Omit { + content: MessageContent[] | string; +} + +interface MessageContent { + type: "text" | "tool_use" | "tool_result"; + text?: string; + id?: string; + name?: string; + content?: string; + tool_use_id?: string; + input?: string | Record; +} export class AnthropicService extends BaseAIService { private client: any = null; @@ -130,7 +145,7 @@ export class AnthropicService extends BaseAIService { .join(''); // Process tool calls if any are present in the response - let toolCalls = null; + let toolCalls: ToolCall[] | null = null; if (response.content) { const toolBlocks = response.content.filter((block: any) => block.type === 'tool_use' || @@ -157,7 +172,7 @@ export class AnthropicService extends BaseAIService { return null; }).filter(Boolean); - log.info(`Extracted ${toolCalls.length} tool calls from Anthropic response`); + log.info(`Extracted ${toolCalls?.length} tool calls from Anthropic response`); } } @@ -406,8 +421,8 @@ export class AnthropicService extends BaseAIService { /** * Format messages for the Anthropic API */ - private formatMessages(messages: Message[]): any[] { - const anthropicMessages: any[] = []; + private formatMessages(messages: Message[]): AnthropicMessage[] { + const anthropicMessages: AnthropicMessage[] = []; // Process each message for (const msg of messages) { @@ -424,7 +439,7 @@ export class AnthropicService extends BaseAIService { // Assistant messages need special handling for tool_calls if (msg.tool_calls && msg.tool_calls.length > 0) { // Create content blocks array for tool calls - const content = []; + const content: MessageContent[] = []; // Add text content if present if (msg.content) { diff --git a/apps/server/src/services/llm/providers/providers.ts b/apps/server/src/services/llm/providers/providers.ts index 5fcd4286a..91295ffe0 100644 --- a/apps/server/src/services/llm/providers/providers.ts +++ b/apps/server/src/services/llm/providers/providers.ts @@ -181,7 +181,7 @@ export async function updateEmbeddingProviderConfig( } // Build update query parts - const updates = []; + const updates: string[] = []; const params: any[] = []; if (priority !== undefined) { diff --git a/apps/server/src/services/llm/tools/content_extraction_tool.ts b/apps/server/src/services/llm/tools/content_extraction_tool.ts index 0a1a18cd7..52c1409e9 100644 --- a/apps/server/src/services/llm/tools/content_extraction_tool.ts +++ b/apps/server/src/services/llm/tools/content_extraction_tool.ts @@ -8,6 +8,26 @@ import type { Tool, ToolHandler } from './tool_interfaces.js'; import log from '../../log.js'; import becca from '../../../becca/becca.js'; +interface CodeBlock { + code: string; + language?: string; +} + +interface Heading { + text: string; + level: number; // 1 for H1, 2 for H2, etc. +} + +interface List { + type: "unordered" | "ordered"; + items: string[]; +} + +interface Table { + headers: string[]; + rows: string[][]; +} + /** * Definition of the content extraction tool */ @@ -137,8 +157,8 @@ export class ContentExtractionTool implements ToolHandler { /** * Extract lists from HTML content */ - private extractLists(content: string): Array<{ type: string, items: string[] }> { - const lists = []; + private extractLists(content: string): List[] { + const lists: List[] = []; // Extract unordered lists const ulRegex = /]*>([\s\S]*?)<\/ul>/gi; @@ -179,7 +199,7 @@ export class ContentExtractionTool implements ToolHandler { * Extract list items from list content */ private extractListItems(listContent: string): string[] { - const items = []; + const items: string[] = []; const itemRegex = /]*>([\s\S]*?)<\/li>/gi; let itemMatch; @@ -196,15 +216,15 @@ export class ContentExtractionTool implements ToolHandler { /** * Extract tables from HTML content */ - private extractTables(content: string): Array<{ headers: string[], rows: string[][] }> { - const tables = []; + private extractTables(content: string): Table[] { + const tables: Table[] = []; const tableRegex = /]*>([\s\S]*?)<\/table>/gi; - let tableMatch; + let tableMatch: RegExpExecArray | null; while ((tableMatch = tableRegex.exec(content)) !== null) { const tableContent = tableMatch[1]; - const headers = []; - const rows = []; + const headers: string[] = []; + const rows: string[][] = []; // Extract table headers const headerRegex = /]*>([\s\S]*?)<\/th>/gi; @@ -218,7 +238,7 @@ export class ContentExtractionTool implements ToolHandler { let rowMatch; while ((rowMatch = rowRegex.exec(tableContent)) !== null) { const rowContent = rowMatch[1]; - const cells = []; + const cells: string[] = []; const cellRegex = /]*>([\s\S]*?)<\/td>/gi; let cellMatch; @@ -246,7 +266,7 @@ export class ContentExtractionTool implements ToolHandler { * Extract headings from HTML content */ private extractHeadings(content: string): Array<{ level: number, text: string }> { - const headings = []; + const headings: Heading[] = []; for (let i = 1; i <= 6; i++) { const headingRegex = new RegExp(`]*>([\\s\\S]*?)<\/h${i}>`, 'gi'); @@ -270,7 +290,7 @@ export class ContentExtractionTool implements ToolHandler { * Extract code blocks from HTML content */ private extractCodeBlocks(content: string): Array<{ language?: string, code: string }> { - const codeBlocks = []; + const codeBlocks: CodeBlock[] = []; // Look for
     and  blocks
             const preRegex = /]*>([\s\S]*?)<\/pre>/gi;
    diff --git a/apps/server/src/services/llm/tools/note_creation_tool.ts b/apps/server/src/services/llm/tools/note_creation_tool.ts
    index 9633880e4..41e608029 100644
    --- a/apps/server/src/services/llm/tools/note_creation_tool.ts
    +++ b/apps/server/src/services/llm/tools/note_creation_tool.ts
    @@ -9,6 +9,7 @@ import log from '../../log.js';
     import becca from '../../../becca/becca.js';
     import notes from '../../notes.js';
     import attributes from '../../attributes.js';
    +import type { BNote } from '../../backend_script_entrypoint.js';
     
     /**
      * Definition of the note creation tool
    @@ -89,7 +90,7 @@ export class NoteCreationTool implements ToolHandler {
                 log.info(`Executing create_note tool - Title: "${title}", Type: ${type}, ParentNoteId: ${parentNoteId || 'root'}`);
     
                 // Validate parent note exists if specified
    -            let parent = null;
    +            let parent: BNote | null = null;
                 if (parentNoteId) {
                     parent = becca.notes[parentNoteId];
                     if (!parent) {
    diff --git a/apps/server/src/services/llm/tools/relationship_tool.ts b/apps/server/src/services/llm/tools/relationship_tool.ts
    index 09e16f72c..a7023981d 100644
    --- a/apps/server/src/services/llm/tools/relationship_tool.ts
    +++ b/apps/server/src/services/llm/tools/relationship_tool.ts
    @@ -10,6 +10,14 @@ import becca from '../../../becca/becca.js';
     import attributes from '../../attributes.js';
     import aiServiceManager from '../ai_service_manager.js';
     import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
    +import type { Backlink, RelatedNote } from '../embeddings/embeddings_interface.js';
    +
    +interface Suggestion {
    +    targetNoteId: string;
    +    targetTitle: string;
    +    similarity: number;
    +    suggestedRelation: string;
    +}
     
     /**
      * Definition of the relationship tool
    @@ -180,7 +188,7 @@ export class RelationshipTool implements ToolHandler {
                     .filter((attr: any) => attr.type === 'relation')
                     .slice(0, limit);
     
    -            const outgoingRelations = [];
    +            const outgoingRelations: RelatedNote[] = [];
     
                 for (const attr of outgoingAttributes) {
                     const targetNote = becca.notes[attr.value];
    @@ -196,7 +204,7 @@ export class RelationshipTool implements ToolHandler {
     
                 // Get incoming relationships (where this note is the target)
                 // Since becca.findNotesWithRelation doesn't exist, use attributes to find notes with relation
    -            const incomingRelations = [];
    +            const incomingRelations: Backlink[] = [];
     
                 // Find all attributes of type relation that point to this note
                 const relationAttributes = sourceNote.getTargetRelations();
    @@ -321,7 +329,7 @@ export class RelationshipTool implements ToolHandler {
                 const sourceContent = await sourceNote.getContent();
     
                 // Prepare suggestions
    -            const suggestions = [];
    +            const suggestions: Suggestion[] = [];
     
                 for (const relatedNote of relatedResult.relatedNotes) {
                     try {
    diff --git a/apps/server/src/services/notes.ts b/apps/server/src/services/notes.ts
    index cbb3c90c1..f06e07ed8 100644
    --- a/apps/server/src/services/notes.ts
    +++ b/apps/server/src/services/notes.ts
    @@ -755,7 +755,7 @@ function updateNoteData(noteId: string, content: string, attachments: Attachment
     function undeleteNote(noteId: string, taskContext: TaskContext) {
         const noteRow = sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]);
     
    -    if (!noteRow.isDeleted) {
    +    if (!noteRow.isDeleted || !noteRow.deleteId) {
             log.error(`Note '${noteId}' is not deleted and thus cannot be undeleted.`);
             return;
         }
    diff --git a/apps/server/src/services/script.ts b/apps/server/src/services/script.ts
    index aedab83bf..1afc3b763 100644
    --- a/apps/server/src/services/script.ts
    +++ b/apps/server/src/services/script.ts
    @@ -118,7 +118,7 @@ function getParams(params?: ScriptParams) {
     }
     
     function getScriptBundleForFrontend(note: BNote, script?: string, params?: ScriptParams) {
    -    let overrideContent = null;
    +    let overrideContent: string | null = null;
     
         if (script) {
             overrideContent = `return (${script}\r\n)(${getParams(params)})`;
    @@ -170,7 +170,7 @@ function getScriptBundle(note: BNote, root: boolean = true, scriptEnv: string |
     
         includedNoteIds.push(note.noteId);
     
    -    const modules = [];
    +    const modules: BNote[] = [];
     
         for (const child of note.getChildNotes()) {
             const childBundle = getScriptBundle(child, false, scriptEnv, includedNoteIds);
    diff --git a/apps/server/src/services/search/expressions/note_flat_text.ts b/apps/server/src/services/search/expressions/note_flat_text.ts
    index 163dd38a0..1f4d4d87a 100644
    --- a/apps/server/src/services/search/expressions/note_flat_text.ts
    +++ b/apps/server/src/services/search/expressions/note_flat_text.ts
    @@ -52,7 +52,7 @@ class NoteFlatTextExp extends Expression {
                     return;
                 }
     
    -            const foundAttrTokens = [];
    +            const foundAttrTokens: string[] = [];
     
                 for (const token of remainingTokens) {
                     if (note.type.includes(token) || note.mime.includes(token)) {
    @@ -73,7 +73,7 @@ class NoteFlatTextExp extends Expression {
     
                 for (const parentNote of note.parents) {
                     const title = normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
    -                const foundTokens = foundAttrTokens.slice();
    +                const foundTokens: string[] = foundAttrTokens.slice();
     
                     for (const token of remainingTokens) {
                         if (title.includes(token)) {
    @@ -100,7 +100,7 @@ class NoteFlatTextExp extends Expression {
                     continue;
                 }
     
    -            const foundAttrTokens = [];
    +            const foundAttrTokens: string[] = [];
     
                 for (const token of this.tokens) {
                     if (note.type.includes(token) || note.mime.includes(token)) {
    @@ -153,7 +153,7 @@ class NoteFlatTextExp extends Expression {
          * Returns noteIds which have at least one matching tokens
          */
         getCandidateNotes(noteSet: NoteSet): BNote[] {
    -        const candidateNotes = [];
    +        const candidateNotes: BNote[] = [];
     
             for (const note of noteSet.notes) {
                 for (const token of this.tokens) {
    diff --git a/apps/server/src/services/search/services/parse.ts b/apps/server/src/services/search/services/parse.ts
    index c454aef7e..6cfaad6e6 100644
    --- a/apps/server/src/services/search/services/parse.ts
    +++ b/apps/server/src/services/search/services/parse.ts
    @@ -294,11 +294,11 @@ function getExpression(tokens: TokenData[], searchContext: SearchContext, level
                 valueExtractor: ValueExtractor;
                 direction: string;
             }[] = [];
    -        let limit;
    +        let limit: number | undefined = undefined;
     
             if (tokens[i].token === "orderby") {
                 do {
    -                const propertyPath = [];
    +                const propertyPath: string[] = [];
                     let direction = "asc";
     
                     do {
    diff --git a/apps/server/src/services/search/services/search.ts b/apps/server/src/services/search/services/search.ts
    index 3e1020e7a..7c53546e7 100644
    --- a/apps/server/src/services/search/services/search.ts
    +++ b/apps/server/src/services/search/services/search.ts
    @@ -36,7 +36,7 @@ function searchFromNote(note: BNote): SearchNoteResult {
     
         const searchScript = note.getRelationValue("searchScript");
         const searchString = note.getLabelValue("searchString") || "";
    -    let error = null;
    +    let error: string | null = null;
     
         if (searchScript) {
             searchResultNoteIds = searchFromRelation(note, "searchScript");
    diff --git a/apps/server/src/share/shaca/shaca-interface.ts b/apps/server/src/share/shaca/shaca-interface.ts
    index 65b0585b7..1d796b302 100644
    --- a/apps/server/src/share/shaca/shaca-interface.ts
    +++ b/apps/server/src/share/shaca/shaca-interface.ts
    @@ -43,7 +43,7 @@ export default class Shaca {
         }
     
         getNotes(noteIds: string[], ignoreMissing = false) {
    -        const filteredNotes = [];
    +        const filteredNotes: SNote[] = [];
     
             for (const noteId of noteIds) {
                 const note = this.notes[noteId];
    diff --git a/apps/server/tsconfig.app.json b/apps/server/tsconfig.app.json
    index e5ff1c88f..eb7f102aa 100644
    --- a/apps/server/tsconfig.app.json
    +++ b/apps/server/tsconfig.app.json
    @@ -5,7 +5,6 @@
         "moduleResolution": "bundler",
         "target": "ES2020",
         "outDir": "dist",
    -    "strict": false,
         "types": [
           "node",
           "express"
    @@ -14,7 +13,8 @@
         "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
       },
       "include": [
    -    "src/**/*.ts"
    +    "src/**/*.ts",
    +    "package.json"
       ],
       "exclude": [
         "eslint.config.js",
    diff --git a/apps/server/tsconfig.spec.json b/apps/server/tsconfig.spec.json
    index 51c5a98fc..4f8607e4b 100644
    --- a/apps/server/tsconfig.spec.json
    +++ b/apps/server/tsconfig.spec.json
    @@ -1,6 +1,9 @@
     {
       "extends": "../../tsconfig.base.json",
       "compilerOptions": {
    +    "module": "ESNext",
    +    "moduleResolution": "bundler",
    +    "target": "ES2020",
         "outDir": "./out-tsc/vitest",
         "types": [
           "vitest/globals",
    @@ -24,6 +27,7 @@
         "src/**/*.test.jsx",
         "src/**/*.spec.jsx",
         "src/**/*.d.ts",
    -    "src/**/*.ts"
    +    "src/**/*.ts",
    +    "package.json"
       ]
     }
    diff --git a/apps/server/vite.config.ts b/apps/server/vite.config.mts
    similarity index 100%
    rename from apps/server/vite.config.ts
    rename to apps/server/vite.config.mts
    diff --git a/nx.json b/nx.json
    index b83107856..c457d117b 100644
    --- a/nx.json
    +++ b/nx.json
    @@ -62,16 +62,6 @@
         }
       ],
       "targetDefaults": {
    -    "@nx/js:swc": {
    -      "cache": true,
    -      "dependsOn": [
    -        "^build"
    -      ],
    -      "inputs": [
    -        "production",
    -        "^production"
    -      ]
    -    },
         "test": {
           "dependsOn": [
             "^build"
    diff --git a/package.json b/package.json
    index 614e38fdb..f9b066afc 100644
    --- a/package.json
    +++ b/package.json
    @@ -38,10 +38,6 @@
         "@nx/vite": "21.1.2",
         "@nx/web": "21.1.2",
         "@playwright/test": "^1.36.0",
    -    "@swc-node/register": "~1.10.0",
    -    "@swc/cli": "~0.7.0",
    -    "@swc/core": "~1.11.0",
    -    "@swc/helpers": "~0.5.11",
         "@triliumnext/server": "workspace:*",
         "@types/express": "^4.17.21",
         "@types/node": "22.15.24",
    @@ -59,7 +55,6 @@
         "jsonc-eslint-parser": "^2.1.0",
         "nx": "21.1.2",
         "react-refresh": "^0.17.0",
    -    "swc-loader": "0.2.6",
         "tslib": "^2.3.0",
         "tsx": "4.19.4",
         "typescript": "~5.8.0",
    diff --git a/packages/ckeditor5/src/augmentation.ts b/packages/ckeditor5/src/augmentation.ts
    index da5e1c915..c1018db37 100644
    --- a/packages/ckeditor5/src/augmentation.ts
    +++ b/packages/ckeditor5/src/augmentation.ts
    @@ -22,28 +22,3 @@ declare global {
             importMarkdownInline(): void;
         }
     }
    -
    -declare module "ckeditor5" {
    -    interface Editor {
    -        getSelectedHtml(): string;
    -        removeSelection(): Promise;
    -    }
    -
    -    interface EditorConfig {
    -        syntaxHighlighting?: {
    -            loadHighlightJs: () => Promise;
    -            mapLanguageName(mimeType: string): string;
    -            defaultMimeType: string;
    -            enabled: boolean;
    -        },
    -        moveBlockUp?: {
    -            keystroke: string[];
    -        },
    -        moveBlockDown?: {
    -            keystroke: string[];
    -        },
    -        clipboard?: {
    -            copy(text: string): void;
    -        }
    -    }
    -}
    diff --git a/packages/ckeditor5/src/index.ts b/packages/ckeditor5/src/index.ts
    index 8dc0e3611..117d14574 100644
    --- a/packages/ckeditor5/src/index.ts
    +++ b/packages/ckeditor5/src/index.ts
    @@ -5,6 +5,10 @@ import { BalloonEditor, DecoupledEditor, FindAndReplaceEditing, FindCommand } fr
     export { EditorWatchdog } from "ckeditor5";
     export type { EditorConfig, MentionFeed, MentionFeedObjectItem, Node, Position, Element, WatchdogConfig } from "ckeditor5";
     
    +// Import with sideffects to ensure that type augmentations are present.
    +import "@triliumnext/ckeditor5-math";
    +import "@triliumnext/ckeditor5-mermaid";
    +
     /**
      * Short-hand for the CKEditor classes supported by Trilium for text editing.
      * Specialized editors such as the {@link AttributeEditor} are not included.
    @@ -43,3 +47,28 @@ export class PopupEditor extends BalloonEditor {
             return POPUP_EDITOR_PLUGINS;
         }
     }
    +
    +declare module "ckeditor5" {
    +    interface Editor {
    +        getSelectedHtml(): string;
    +        removeSelection(): Promise;
    +    }
    +
    +    interface EditorConfig {
    +        syntaxHighlighting?: {
    +            loadHighlightJs: () => Promise;
    +            mapLanguageName(mimeType: string): string;
    +            defaultMimeType: string;
    +            enabled: boolean;
    +        },
    +        moveBlockUp?: {
    +            keystroke: string[];
    +        },
    +        moveBlockDown?: {
    +            keystroke: string[];
    +        },
    +        clipboard?: {
    +            copy(text: string): void;
    +        }
    +    }
    +}
    diff --git a/packages/ckeditor5/src/plugins/syntax_highlighting/index.ts b/packages/ckeditor5/src/plugins/syntax_highlighting/index.ts
    index 894939374..ef6430331 100644
    --- a/packages/ckeditor5/src/plugins/syntax_highlighting/index.ts
    +++ b/packages/ckeditor5/src/plugins/syntax_highlighting/index.ts
    @@ -1,7 +1,12 @@
    -import type { Element, Writer } from "ckeditor5";
    +import type { Element, Position, Writer } from "ckeditor5";
     import type { Node, Editor } from "ckeditor5";
     import { Plugin } from "ckeditor5";
     
    +interface SpanStackEntry {
    +    className: string;
    +    posStart: Position;
    +}
    +
     /*
      * This code is an adaptation of https://github.com/antoniotejada/Trilium-SyntaxHighlightWidget with additional improvements, such as:
      *
    @@ -234,10 +239,10 @@ export default class SyntaxHighlighting extends Plugin {
     
             let iHtml = 0;
             let html = highlightRes.value;
    -        let spanStack = [];
    +        let spanStack: SpanStackEntry[] = [];
             let iChild = -1;
             let childText = "";
    -        let child = null;
    +        let child: Node | null = null;
             let iChildText = 0;
     
             while (iHtml < html.length) {
    diff --git a/packages/codemirror/src/extensions/custom_tab.ts b/packages/codemirror/src/extensions/custom_tab.ts
    index 2355ce90f..c8ced1df2 100644
    --- a/packages/codemirror/src/extensions/custom_tab.ts
    +++ b/packages/codemirror/src/extensions/custom_tab.ts
    @@ -1,5 +1,5 @@
     import { indentLess, indentMore } from "@codemirror/commands";
    -import { EditorSelection, EditorState, type ChangeSpec } from "@codemirror/state";
    +import { EditorSelection, EditorState, SelectionRange, type ChangeSpec } from "@codemirror/state";
     import type { KeyBinding } from "@codemirror/view";
     
     /**
    @@ -19,8 +19,8 @@ const smartIndentWithTab: KeyBinding[] = [
                 }
     
                 const { selection } = state;
    -            const changes = [];
    -            const newSelections = [];
    +            const changes: ChangeSpec[] = [];
    +            const newSelections: SelectionRange[] = [];
     
                 // Step 1: Handle non-empty selections → replace with tab
                 if (selection.ranges.some(range => !range.empty)) {
    diff --git a/packages/codemirror/tsconfig.spec.json b/packages/codemirror/tsconfig.spec.json
    index fbd68ed60..f7f4942ed 100644
    --- a/packages/codemirror/tsconfig.spec.json
    +++ b/packages/codemirror/tsconfig.spec.json
    @@ -23,6 +23,6 @@
         "src/**/*.spec.js",
         "src/**/*.test.jsx",
         "src/**/*.spec.jsx",
    -    "src/**/*.d.ts"
    +    "src/**/*.ts"
       ]
     }
    diff --git a/packages/commons/.swcrc b/packages/commons/.swcrc
    deleted file mode 100644
    index bc22f28ee..000000000
    --- a/packages/commons/.swcrc
    +++ /dev/null
    @@ -1,22 +0,0 @@
    -{
    -  "jsc": {
    -    "target": "es2017",
    -    "parser": {
    -      "syntax": "typescript",
    -      "decorators": true,
    -      "dynamicImport": true
    -    },
    -    "transform": {
    -      "decoratorMetadata": true,
    -      "legacyDecorator": true
    -    },
    -    "keepClassNames": true,
    -    "externalHelpers": true,
    -    "loose": true
    -  },
    -  "module": {
    -    "type": "es6"
    -  },
    -  "sourceMaps": true,
    -  "exclude": ["jest.config.ts",".*\\.spec.tsx?$",".*\\.test.tsx?$","./src/jest-setup.ts$","./**/jest-setup.ts$"]
    -}
    diff --git a/packages/commons/package.json b/packages/commons/package.json
    index 09312903b..116d182e9 100644
    --- a/packages/commons/package.json
    +++ b/packages/commons/package.json
    @@ -4,16 +4,16 @@
       "description": "Shared library between the clients (e.g. browser, Electron) and the server, mostly for type definitions and utility methods.",
       "private": true,
       "type": "module",
    -  "main": "./dist/index.js",
    -  "module": "./dist/index.js",
    +  "main": "./dist/main.js",
    +  "module": "./dist/main.js",
       "types": "./dist/index.d.ts",
       "exports": {
         "./package.json": "./package.json",
         ".": {
           "development": "./src/index.ts",
           "types": "./dist/index.d.ts",
    -      "import": "./dist/index.js",
    -      "default": "./dist/index.js"
    +      "import": "./dist/main.js",
    +      "default": "./dist/main.js"
         }
       },
       "license": "AGPL-3.0-only",
    @@ -27,21 +27,31 @@
         "sourceRoot": "packages/commons/src",
         "targets": {
           "build": {
    -        "executor": "@nx/js:swc",
    +        "executor": "@nx/esbuild:esbuild",
             "outputs": [
               "{options.outputPath}"
             ],
    +        "defaultConfiguration": "production",
             "options": {
    -          "outputPath": "packages/commons/dist",
               "main": "packages/commons/src/index.ts",
    +          "outputPath": "packages/commons/dist",
    +          "outputFileName": "main.js",
               "tsConfig": "packages/commons/tsconfig.lib.json",
    -          "skipTypeCheck": true,
    -          "stripLeadingPaths": true
    +          "platform": "node",
    +          "format": [
    +            "esm"
    +          ],
    +          "declarationRootDir": "packages/commons/src"
    +        },
    +        "configurations": {
    +          "development": {
    +            "minify": false
    +          },
    +          "production": {
    +            "minify": true
    +          }
             }
           }
         }
    -  },
    -  "dependencies": {
    -    "@swc/helpers": "~0.5.11"
       }
    -}
    \ No newline at end of file
    +}
    diff --git a/packages/commons/src/lib/rows.ts b/packages/commons/src/lib/rows.ts
    index 6b7d73a7e..735146eeb 100644
    --- a/packages/commons/src/lib/rows.ts
    +++ b/packages/commons/src/lib/rows.ts
    @@ -126,17 +126,17 @@ export type NoteType = (typeof ALLOWED_NOTE_TYPES)[number];
     
     export interface NoteRow {
         noteId: string;
    -    deleteId: string;
    +    deleteId?: string;
         title: string;
         type: NoteType;
         mime: string;
    -    isProtected: boolean;
    -    isDeleted: boolean;
    -    blobId: string;
    -    dateCreated: string;
    -    dateModified: string;
    -    utcDateCreated: string;
    -    utcDateModified: string;
    +    isProtected?: boolean;
    +    isDeleted?: boolean;
    +    blobId?: string;
    +    dateCreated?: string;
    +    dateModified?: string;
    +    utcDateCreated?: string;
    +    utcDateModified?: string;
         content?: string | Buffer;
     }
     
    diff --git a/packages/commons/src/lib/test-utils.ts b/packages/commons/src/lib/test-utils.ts
    index 8c796a3d3..edfe90ef0 100644
    --- a/packages/commons/src/lib/test-utils.ts
    +++ b/packages/commons/src/lib/test-utils.ts
    @@ -44,7 +44,7 @@ export function trimIndentation(strings: TemplateStringsArray, ...values: any[])
     
         // Trim the indentation of the first line in all the lines.
         const lines = str.split("\n");
    -    const output = [];
    +    const output: string[] = [];
         for (let i = 0; i < lines.length; i++) {
             let numSpacesLine = 0;
             while (str.charAt(numSpacesLine) == " " && numSpacesLine < str.length) {
    diff --git a/packages/turndown-plugin-gfm/package.json b/packages/turndown-plugin-gfm/package.json
    index 6834788b2..733e8d92f 100644
    --- a/packages/turndown-plugin-gfm/package.json
    +++ b/packages/turndown-plugin-gfm/package.json
    @@ -56,9 +56,6 @@
           }
         }
       },
    -  "dependencies": {
    -    "@swc/helpers": "~0.5.11"
    -  },
       "devDependencies": {
         "turndown": "7.2.0",
         "turndown-attendant": "0.0.3"
    diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
    index a2ca81b8a..678be5383 100644
    --- a/pnpm-lock.yaml
    +++ b/pnpm-lock.yaml
    @@ -73,18 +73,6 @@ importers:
           '@playwright/test':
             specifier: ^1.36.0
             version: 1.52.0
    -      '@swc-node/register':
    -        specifier: ~1.10.0
    -        version: 1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3)
    -      '@swc/cli':
    -        specifier: ~0.7.0
    -        version: 0.7.7(@swc/core@1.11.29(@swc/helpers@0.5.17))(chokidar@4.0.3)
    -      '@swc/core':
    -        specifier: ~1.11.0
    -        version: 1.11.29(@swc/helpers@0.5.17)
    -      '@swc/helpers':
    -        specifier: ~0.5.11
    -        version: 0.5.17
           '@triliumnext/server':
             specifier: workspace:*
             version: link:apps/server
    @@ -136,9 +124,6 @@ importers:
           react-refresh:
             specifier: ^0.17.0
             version: 0.17.0
    -      swc-loader:
    -        specifier: 0.2.6
    -        version: 0.2.6(@swc/core@1.11.29(@swc/helpers@0.5.17))(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4))
           tslib:
             specifier: ^2.3.0
             version: 2.8.1
    @@ -1261,11 +1246,7 @@ importers:
             specifier: 9.27.0
             version: 9.27.0
     
    -  packages/commons:
    -    dependencies:
    -      '@swc/helpers':
    -        specifier: ~0.5.11
    -        version: 0.5.17
    +  packages/commons: {}
     
       packages/express-partial-content:
         dependencies:
    @@ -1292,10 +1273,6 @@ importers:
             version: 1.2.0
     
       packages/turndown-plugin-gfm:
    -    dependencies:
    -      '@swc/helpers':
    -        specifier: ~0.5.11
    -        version: 0.5.17
         devDependencies:
           turndown:
             specifier: 7.2.0
    @@ -3285,106 +3262,6 @@ packages:
         resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==}
         engines: {node: '>=18'}
     
    -  '@napi-rs/nice-android-arm-eabi@1.0.1':
    -    resolution: {integrity: sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==}
    -    engines: {node: '>= 10'}
    -    cpu: [arm]
    -    os: [android]
    -
    -  '@napi-rs/nice-android-arm64@1.0.1':
    -    resolution: {integrity: sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA==}
    -    engines: {node: '>= 10'}
    -    cpu: [arm64]
    -    os: [android]
    -
    -  '@napi-rs/nice-darwin-arm64@1.0.1':
    -    resolution: {integrity: sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA==}
    -    engines: {node: '>= 10'}
    -    cpu: [arm64]
    -    os: [darwin]
    -
    -  '@napi-rs/nice-darwin-x64@1.0.1':
    -    resolution: {integrity: sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ==}
    -    engines: {node: '>= 10'}
    -    cpu: [x64]
    -    os: [darwin]
    -
    -  '@napi-rs/nice-freebsd-x64@1.0.1':
    -    resolution: {integrity: sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw==}
    -    engines: {node: '>= 10'}
    -    cpu: [x64]
    -    os: [freebsd]
    -
    -  '@napi-rs/nice-linux-arm-gnueabihf@1.0.1':
    -    resolution: {integrity: sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q==}
    -    engines: {node: '>= 10'}
    -    cpu: [arm]
    -    os: [linux]
    -
    -  '@napi-rs/nice-linux-arm64-gnu@1.0.1':
    -    resolution: {integrity: sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA==}
    -    engines: {node: '>= 10'}
    -    cpu: [arm64]
    -    os: [linux]
    -
    -  '@napi-rs/nice-linux-arm64-musl@1.0.1':
    -    resolution: {integrity: sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==}
    -    engines: {node: '>= 10'}
    -    cpu: [arm64]
    -    os: [linux]
    -
    -  '@napi-rs/nice-linux-ppc64-gnu@1.0.1':
    -    resolution: {integrity: sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==}
    -    engines: {node: '>= 10'}
    -    cpu: [ppc64]
    -    os: [linux]
    -
    -  '@napi-rs/nice-linux-riscv64-gnu@1.0.1':
    -    resolution: {integrity: sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==}
    -    engines: {node: '>= 10'}
    -    cpu: [riscv64]
    -    os: [linux]
    -
    -  '@napi-rs/nice-linux-s390x-gnu@1.0.1':
    -    resolution: {integrity: sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==}
    -    engines: {node: '>= 10'}
    -    cpu: [s390x]
    -    os: [linux]
    -
    -  '@napi-rs/nice-linux-x64-gnu@1.0.1':
    -    resolution: {integrity: sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==}
    -    engines: {node: '>= 10'}
    -    cpu: [x64]
    -    os: [linux]
    -
    -  '@napi-rs/nice-linux-x64-musl@1.0.1':
    -    resolution: {integrity: sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==}
    -    engines: {node: '>= 10'}
    -    cpu: [x64]
    -    os: [linux]
    -
    -  '@napi-rs/nice-win32-arm64-msvc@1.0.1':
    -    resolution: {integrity: sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==}
    -    engines: {node: '>= 10'}
    -    cpu: [arm64]
    -    os: [win32]
    -
    -  '@napi-rs/nice-win32-ia32-msvc@1.0.1':
    -    resolution: {integrity: sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw==}
    -    engines: {node: '>= 10'}
    -    cpu: [ia32]
    -    os: [win32]
    -
    -  '@napi-rs/nice-win32-x64-msvc@1.0.1':
    -    resolution: {integrity: sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg==}
    -    engines: {node: '>= 10'}
    -    cpu: [x64]
    -    os: [win32]
    -
    -  '@napi-rs/nice@1.0.1':
    -    resolution: {integrity: sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ==}
    -    engines: {node: '>= 10'}
    -
       '@napi-rs/wasm-runtime@0.2.4':
         resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==}
     
    @@ -4252,9 +4129,6 @@ packages:
       '@scarf/scarf@1.4.0':
         resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==}
     
    -  '@sec-ant/readable-stream@0.4.1':
    -    resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
    -
       '@sideway/address@4.1.5':
         resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
     
    @@ -4271,10 +4145,6 @@ packages:
         resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
         engines: {node: '>=10'}
     
    -  '@sindresorhus/is@5.6.0':
    -    resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
    -    engines: {node: '>=14.16'}
    -
       '@sinonjs/commons@3.0.1':
         resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==}
     
    @@ -4303,17 +4173,6 @@ packages:
       '@swc-node/sourcemap-support@0.5.1':
         resolution: {integrity: sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg==}
     
    -  '@swc/cli@0.7.7':
    -    resolution: {integrity: sha512-j4yYm9bx3pxWofaJKX1BFwj/3ngUDynN4UIQ2Xd2h0h/7Gt7zkReBTpDN7g5S13mgAYxacaTHTOUsz18097E8w==}
    -    engines: {node: '>= 16.14.0'}
    -    hasBin: true
    -    peerDependencies:
    -      '@swc/core': ^1.2.66
    -      chokidar: ^4.0.1
    -    peerDependenciesMeta:
    -      chokidar:
    -        optional: true
    -
       '@swc/core-darwin-arm64@1.11.29':
         resolution: {integrity: sha512-whsCX7URzbuS5aET58c75Dloby3Gtj/ITk2vc4WW6pSDQKSPDuONsIcZ7B2ng8oz0K6ttbi4p3H/PNPQLJ4maQ==}
         engines: {node: '>=10'}
    @@ -4396,10 +4255,6 @@ packages:
         resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
         engines: {node: '>=10'}
     
    -  '@szmarczak/http-timer@5.0.1':
    -    resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
    -    engines: {node: '>=14.16'}
    -
       '@testing-library/dom@10.4.0':
         resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
         engines: {node: '>=18'}
    @@ -5155,46 +5010,6 @@ packages:
       '@webcomponents/webcomponentsjs@2.8.0':
         resolution: {integrity: sha512-loGD63sacRzOzSJgQnB9ZAhaQGkN7wl2Zuw7tsphI5Isa0irijrRo6EnJii/GgjGefIFO8AIO7UivzRhFaEk9w==}
     
    -  '@xhmikosr/archive-type@7.0.0':
    -    resolution: {integrity: sha512-sIm84ZneCOJuiy3PpWR5bxkx3HaNt1pqaN+vncUBZIlPZCq8ASZH+hBVdu5H8znR7qYC6sKwx+ie2Q7qztJTxA==}
    -    engines: {node: ^14.14.0 || >=16.0.0}
    -
    -  '@xhmikosr/bin-check@7.0.3':
    -    resolution: {integrity: sha512-4UnCLCs8DB+itHJVkqFp9Zjg+w/205/J2j2wNBsCEAm/BuBmtua2hhUOdAMQE47b1c7P9Xmddj0p+X1XVsfHsA==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/bin-wrapper@13.0.5':
    -    resolution: {integrity: sha512-DT2SAuHDeOw0G5bs7wZbQTbf4hd8pJ14tO0i4cWhRkIJfgRdKmMfkDilpaJ8uZyPA0NVRwasCNAmMJcWA67osw==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/decompress-tar@8.0.1':
    -    resolution: {integrity: sha512-dpEgs0cQKJ2xpIaGSO0hrzz3Kt8TQHYdizHsgDtLorWajuHJqxzot9Hbi0huRxJuAGG2qiHSQkwyvHHQtlE+fg==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/decompress-tarbz2@8.0.2':
    -    resolution: {integrity: sha512-p5A2r/AVynTQSsF34Pig6olt9CvRj6J5ikIhzUd3b57pUXyFDGtmBstcw+xXza0QFUh93zJsmY3zGeNDlR2AQQ==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/decompress-targz@8.0.1':
    -    resolution: {integrity: sha512-mvy5AIDIZjQ2IagMI/wvauEiSNHhu/g65qpdM4EVoYHUJBAmkQWqcPJa8Xzi1aKVTmOA5xLJeDk7dqSjlHq8Mg==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/decompress-unzip@7.0.0':
    -    resolution: {integrity: sha512-GQMpzIpWTsNr6UZbISawsGI0hJ4KA/mz5nFq+cEoPs12UybAqZWKbyIaZZyLbJebKl5FkLpsGBkrplJdjvUoSQ==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/decompress@10.0.1':
    -    resolution: {integrity: sha512-6uHnEEt5jv9ro0CDzqWlFgPycdE+H+kbJnwyxgZregIMLQ7unQSCNVsYG255FoqU8cP46DyggI7F7LohzEl8Ag==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/downloader@15.0.1':
    -    resolution: {integrity: sha512-fiuFHf3Dt6pkX8HQrVBsK0uXtkgkVlhrZEh8b7VgoDqFf+zrgFBPyrwCqE/3nDwn3hLeNz+BsrS7q3mu13Lp1g==}
    -    engines: {node: '>=18'}
    -
    -  '@xhmikosr/os-filter-obj@3.0.0':
    -    resolution: {integrity: sha512-siPY6BD5dQ2SZPl3I0OZBHL27ZqZvLEosObsZRQ1NUB8qcxegwt0T9eKtV96JMFQpIz1elhkzqOg4c/Ri6Dp9A==}
    -    engines: {node: ^14.14.0 || >=16.0.0}
    -
       '@xmldom/xmldom@0.8.10':
         resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
         engines: {node: '>=10.0.0'}
    @@ -5417,9 +5232,6 @@ packages:
       aproba@1.2.0:
         resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
     
    -  arch@3.0.0:
    -    resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==}
    -
       archiver-utils@5.0.2:
         resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==}
         engines: {node: '>= 14'}
    @@ -5659,14 +5471,6 @@ packages:
       big.js@5.2.2:
         resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
     
    -  bin-version-check@5.1.0:
    -    resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==}
    -    engines: {node: '>=12'}
    -
    -  bin-version@6.0.0:
    -    resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==}
    -    engines: {node: '>=12'}
    -
       binary-extensions@2.3.0:
         resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
         engines: {node: '>=8'}
    @@ -5802,14 +5606,6 @@ packages:
         resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
         engines: {node: '>=10.6.0'}
     
    -  cacheable-lookup@7.0.0:
    -    resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
    -    engines: {node: '>=14.16'}
    -
    -  cacheable-request@10.2.14:
    -    resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
    -    engines: {node: '>=14.16'}
    -
       cacheable-request@7.0.4:
         resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
         engines: {node: '>=8'}
    @@ -6107,10 +5903,6 @@ packages:
         resolution: {integrity: sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==}
         engines: {node: '>= 6'}
     
    -  commander@6.2.1:
    -    resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
    -    engines: {node: '>= 6'}
    -
       commander@7.2.0:
         resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
         engines: {node: '>= 10'}
    @@ -6758,10 +6550,6 @@ packages:
       defaults@1.0.4:
         resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
     
    -  defaults@3.0.0:
    -    resolution: {integrity: sha512-RsqXDEAALjfRTro+IFNKpcPCt0/Cy2FqHSIlnomiJp9YGadpQnrtbRpSgN2+np21qHcIKiva4fiOQGjS9/qR/A==}
    -    engines: {node: '>=18'}
    -
       defer-to-connect@2.0.1:
         resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
         engines: {node: '>=10'}
    @@ -7336,10 +7124,6 @@ packages:
         resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
         engines: {node: '>=6'}
     
    -  execa@5.1.1:
    -    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
    -    engines: {node: '>=10'}
    -
       exif-parser@0.1.12:
         resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==}
     
    @@ -7482,10 +7266,6 @@ packages:
         resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==}
         engines: {node: '>=10'}
     
    -  file-type@19.6.0:
    -    resolution: {integrity: sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ==}
    -    engines: {node: '>=18'}
    -
       file-type@20.5.0:
         resolution: {integrity: sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==}
         engines: {node: '>=18'}
    @@ -7504,18 +7284,10 @@ packages:
         resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==}
         engines: {node: '>=4'}
     
    -  filename-reserved-regex@3.0.0:
    -    resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==}
    -    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
    -
       filenamify@4.3.0:
         resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==}
         engines: {node: '>=8'}
     
    -  filenamify@6.0.0:
    -    resolution: {integrity: sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==}
    -    engines: {node: '>=16'}
    -
       fill-range@7.1.1:
         resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
         engines: {node: '>=8'}
    @@ -7548,10 +7320,6 @@ packages:
         resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
         engines: {node: '>=10'}
     
    -  find-versions@5.1.0:
    -    resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==}
    -    engines: {node: '>=12'}
    -
       flat-cache@4.0.1:
         resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
         engines: {node: '>=16'}
    @@ -7601,10 +7369,6 @@ packages:
       form-data-encoder@1.7.2:
         resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
     
    -  form-data-encoder@2.1.4:
    -    resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
    -    engines: {node: '>= 14.17'}
    -
       form-data@3.0.3:
         resolution: {integrity: sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==}
         engines: {node: '>= 6'}
    @@ -7793,14 +7557,6 @@ packages:
         resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
         engines: {node: '>=8'}
     
    -  get-stream@6.0.1:
    -    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
    -    engines: {node: '>=10'}
    -
    -  get-stream@9.0.1:
    -    resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
    -    engines: {node: '>=18'}
    -
       get-symbol-description@1.1.0:
         resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
         engines: {node: '>= 0.4'}
    @@ -7915,10 +7671,6 @@ packages:
         resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
         engines: {node: '>=10.19.0'}
     
    -  got@13.0.0:
    -    resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==}
    -    engines: {node: '>=16'}
    -
       graceful-fs@4.2.11:
         resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
     
    @@ -8123,10 +7875,6 @@ packages:
         resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==}
         engines: {node: '>=10.19.0'}
     
    -  http2-wrapper@2.2.1:
    -    resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
    -    engines: {node: '>=10.19.0'}
    -
       https-proxy-agent@5.0.1:
         resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
         engines: {node: '>= 6'}
    @@ -8135,10 +7883,6 @@ packages:
         resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
         engines: {node: '>= 14'}
     
    -  human-signals@2.1.0:
    -    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
    -    engines: {node: '>=10.17.0'}
    -
       humanize-ms@1.2.1:
         resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
     
    @@ -8272,9 +8016,6 @@ packages:
         resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==}
         engines: {node: ^18.17.0 || >=20.5.0}
     
    -  inspect-with-kind@1.0.5:
    -    resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==}
    -
       internal-slot@1.1.0:
         resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
         engines: {node: '>= 0.4'}
    @@ -8514,10 +8255,6 @@ packages:
         resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
         engines: {node: '>=8'}
     
    -  is-stream@4.0.1:
    -    resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
    -    engines: {node: '>=18'}
    -
       is-string@1.1.1:
         resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
         engines: {node: '>= 0.4'}
    @@ -9118,10 +8855,6 @@ packages:
         resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
         engines: {node: '>=8'}
     
    -  lowercase-keys@3.0.0:
    -    resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
    -    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
    -
       lru-cache@10.4.3:
         resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
     
    @@ -9376,10 +9109,6 @@ packages:
         resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
         engines: {node: '>=10'}
     
    -  mimic-response@4.0.0:
    -    resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
    -    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
    -
       mind-elixir@4.6.0:
         resolution: {integrity: sha512-svl/YkKaIR8vsKhdgA0YRmGCWlVUgyYfUrDIFfnmL599CJDkw5f1gygwbQN8uDyRIRXNl8LZLUqEZmwmxXAPsg==}
     
    @@ -9705,10 +9434,6 @@ packages:
         resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
         engines: {node: '>=10'}
     
    -  normalize-url@8.0.1:
    -    resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
    -    engines: {node: '>=14.16'}
    -
       normalize.css@8.0.1:
         resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==}
     
    @@ -9874,10 +9599,6 @@ packages:
         resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
         engines: {node: '>=8'}
     
    -  p-cancelable@3.0.0:
    -    resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
    -    engines: {node: '>=12.20'}
    -
       p-defer@1.0.0:
         resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==}
         engines: {node: '>=4'}
    @@ -10096,10 +9817,6 @@ packages:
         resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==}
         engines: {node: '>=8'}
     
    -  peek-readable@5.4.2:
    -    resolution: {integrity: sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==}
    -    engines: {node: '>=14.16'}
    -
       peek-readable@7.0.0:
         resolution: {integrity: sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==}
         engines: {node: '>=18'}
    @@ -10141,9 +9858,6 @@ packages:
         resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
         engines: {node: '>= 6'}
     
    -  piscina@4.9.2:
    -    resolution: {integrity: sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==}
    -
       pixelmatch@5.3.0:
         resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==}
         hasBin: true
    @@ -11189,10 +10903,6 @@ packages:
       responselike@2.0.1:
         resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
     
    -  responselike@3.0.0:
    -    resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==}
    -    engines: {node: '>=14.16'}
    -
       resq@1.11.0:
         resolution: {integrity: sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==}
     
    @@ -11493,10 +11203,6 @@ packages:
       secure-compare@3.0.1:
         resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==}
     
    -  seek-bzip@2.0.0:
    -    resolution: {integrity: sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==}
    -    hasBin: true
    -
       select-hose@2.0.0:
         resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==}
     
    @@ -11507,14 +11213,6 @@ packages:
       semver-compare@1.0.0:
         resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
     
    -  semver-regex@4.0.5:
    -    resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==}
    -    engines: {node: '>=12'}
    -
    -  semver-truncate@3.0.0:
    -    resolution: {integrity: sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==}
    -    engines: {node: '>=12'}
    -
       semver@5.7.2:
         resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
         hasBin: true
    @@ -11919,17 +11617,10 @@ packages:
         resolution: {integrity: sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==}
         engines: {node: '>=12'}
     
    -  strip-dirs@3.0.0:
    -    resolution: {integrity: sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==}
    -
       strip-eof@1.0.0:
         resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
         engines: {node: '>=0.10.0'}
     
    -  strip-final-newline@2.0.0:
    -    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
    -    engines: {node: '>=6'}
    -
       strip-json-comments@2.0.1:
         resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
         engines: {node: '>=0.10.0'}
    @@ -11956,10 +11647,6 @@ packages:
         resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==}
         engines: {node: '>=10'}
     
    -  strtok3@9.1.1:
    -    resolution: {integrity: sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==}
    -    engines: {node: '>=16'}
    -
       style-loader@2.0.0:
         resolution: {integrity: sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==}
         engines: {node: '>= 10.13.0'}
    @@ -12106,12 +11793,6 @@ packages:
         peerDependencies:
           express: '>=4.0.0 || >=5.0.0-beta'
     
    -  swc-loader@0.2.6:
    -    resolution: {integrity: sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==}
    -    peerDependencies:
    -      '@swc/core': ^1.2.147
    -      webpack: '>=2'
    -
       symbol-tree@3.2.4:
         resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
     
    @@ -12213,9 +11894,6 @@ packages:
       through2@4.0.2:
         resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
     
    -  through@2.3.8:
    -    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
    -
       thunky@1.1.0:
         resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
     
    @@ -12509,9 +12187,6 @@ packages:
         resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
         engines: {node: '>= 0.4'}
     
    -  unbzip2-stream@1.4.3:
    -    resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
    -
       undici-types@6.19.8:
         resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
     
    @@ -16402,74 +16077,6 @@ snapshots:
           strict-event-emitter: 0.5.1
         optional: true
     
    -  '@napi-rs/nice-android-arm-eabi@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-android-arm64@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-darwin-arm64@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-darwin-x64@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-freebsd-x64@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-arm-gnueabihf@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-arm64-gnu@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-arm64-musl@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-ppc64-gnu@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-riscv64-gnu@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-s390x-gnu@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-x64-gnu@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-linux-x64-musl@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-win32-arm64-msvc@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-win32-ia32-msvc@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice-win32-x64-msvc@1.0.1':
    -    optional: true
    -
    -  '@napi-rs/nice@1.0.1':
    -    optionalDependencies:
    -      '@napi-rs/nice-android-arm-eabi': 1.0.1
    -      '@napi-rs/nice-android-arm64': 1.0.1
    -      '@napi-rs/nice-darwin-arm64': 1.0.1
    -      '@napi-rs/nice-darwin-x64': 1.0.1
    -      '@napi-rs/nice-freebsd-x64': 1.0.1
    -      '@napi-rs/nice-linux-arm-gnueabihf': 1.0.1
    -      '@napi-rs/nice-linux-arm64-gnu': 1.0.1
    -      '@napi-rs/nice-linux-arm64-musl': 1.0.1
    -      '@napi-rs/nice-linux-ppc64-gnu': 1.0.1
    -      '@napi-rs/nice-linux-riscv64-gnu': 1.0.1
    -      '@napi-rs/nice-linux-s390x-gnu': 1.0.1
    -      '@napi-rs/nice-linux-x64-gnu': 1.0.1
    -      '@napi-rs/nice-linux-x64-musl': 1.0.1
    -      '@napi-rs/nice-win32-arm64-msvc': 1.0.1
    -      '@napi-rs/nice-win32-ia32-msvc': 1.0.1
    -      '@napi-rs/nice-win32-x64-msvc': 1.0.1
    -    optional: true
    -
       '@napi-rs/wasm-runtime@0.2.4':
         dependencies:
           '@emnapi/core': 1.4.3
    @@ -17451,8 +17058,6 @@ snapshots:
     
       '@scarf/scarf@1.4.0': {}
     
    -  '@sec-ant/readable-stream@0.4.1': {}
    -
       '@sideway/address@4.1.5':
         dependencies:
           '@hapi/hoek': 9.3.0
    @@ -17465,8 +17070,6 @@ snapshots:
     
       '@sindresorhus/is@4.6.0': {}
     
    -  '@sindresorhus/is@5.6.0': {}
    -
       '@sinonjs/commons@3.0.1':
         dependencies:
           type-detect: 4.0.8
    @@ -17487,6 +17090,7 @@ snapshots:
         dependencies:
           '@swc/core': 1.11.29(@swc/helpers@0.5.17)
           '@swc/types': 0.1.21
    +    optional: true
     
       '@swc-node/register@1.10.10(@swc/core@1.11.29(@swc/helpers@0.5.17))(@swc/types@0.1.21)(typescript@5.8.3)':
         dependencies:
    @@ -17502,26 +17106,13 @@ snapshots:
         transitivePeerDependencies:
           - '@swc/types'
           - supports-color
    +    optional: true
     
       '@swc-node/sourcemap-support@0.5.1':
         dependencies:
           source-map-support: 0.5.21
           tslib: 2.8.1
    -
    -  '@swc/cli@0.7.7(@swc/core@1.11.29(@swc/helpers@0.5.17))(chokidar@4.0.3)':
    -    dependencies:
    -      '@swc/core': 1.11.29(@swc/helpers@0.5.17)
    -      '@swc/counter': 0.1.3
    -      '@xhmikosr/bin-wrapper': 13.0.5
    -      commander: 8.3.0
    -      fast-glob: 3.3.3
    -      minimatch: 9.0.5
    -      piscina: 4.9.2
    -      semver: 7.7.1
    -      slash: 3.0.0
    -      source-map: 0.7.4
    -    optionalDependencies:
    -      chokidar: 4.0.3
    +    optional: true
     
       '@swc/core-darwin-arm64@1.11.29':
         optional: true
    @@ -17584,10 +17175,6 @@ snapshots:
         dependencies:
           defer-to-connect: 2.0.1
     
    -  '@szmarczak/http-timer@5.0.1':
    -    dependencies:
    -      defer-to-connect: 2.0.1
    -
       '@testing-library/dom@10.4.0':
         dependencies:
           '@babel/code-frame': 7.26.2
    @@ -18597,74 +18184,6 @@ snapshots:
     
       '@webcomponents/webcomponentsjs@2.8.0': {}
     
    -  '@xhmikosr/archive-type@7.0.0':
    -    dependencies:
    -      file-type: 19.6.0
    -
    -  '@xhmikosr/bin-check@7.0.3':
    -    dependencies:
    -      execa: 5.1.1
    -      isexe: 2.0.0
    -
    -  '@xhmikosr/bin-wrapper@13.0.5':
    -    dependencies:
    -      '@xhmikosr/bin-check': 7.0.3
    -      '@xhmikosr/downloader': 15.0.1
    -      '@xhmikosr/os-filter-obj': 3.0.0
    -      bin-version-check: 5.1.0
    -
    -  '@xhmikosr/decompress-tar@8.0.1':
    -    dependencies:
    -      file-type: 19.6.0
    -      is-stream: 2.0.1
    -      tar-stream: 3.1.7
    -
    -  '@xhmikosr/decompress-tarbz2@8.0.2':
    -    dependencies:
    -      '@xhmikosr/decompress-tar': 8.0.1
    -      file-type: 19.6.0
    -      is-stream: 2.0.1
    -      seek-bzip: 2.0.0
    -      unbzip2-stream: 1.4.3
    -
    -  '@xhmikosr/decompress-targz@8.0.1':
    -    dependencies:
    -      '@xhmikosr/decompress-tar': 8.0.1
    -      file-type: 19.6.0
    -      is-stream: 2.0.1
    -
    -  '@xhmikosr/decompress-unzip@7.0.0':
    -    dependencies:
    -      file-type: 19.6.0
    -      get-stream: 6.0.1
    -      yauzl: 3.2.0
    -
    -  '@xhmikosr/decompress@10.0.1':
    -    dependencies:
    -      '@xhmikosr/decompress-tar': 8.0.1
    -      '@xhmikosr/decompress-tarbz2': 8.0.2
    -      '@xhmikosr/decompress-targz': 8.0.1
    -      '@xhmikosr/decompress-unzip': 7.0.0
    -      graceful-fs: 4.2.11
    -      make-dir: 4.0.0
    -      strip-dirs: 3.0.0
    -
    -  '@xhmikosr/downloader@15.0.1':
    -    dependencies:
    -      '@xhmikosr/archive-type': 7.0.0
    -      '@xhmikosr/decompress': 10.0.1
    -      content-disposition: 0.5.4
    -      defaults: 3.0.0
    -      ext-name: 5.0.0
    -      file-type: 19.6.0
    -      filenamify: 6.0.0
    -      get-stream: 6.0.1
    -      got: 13.0.0
    -
    -  '@xhmikosr/os-filter-obj@3.0.0':
    -    dependencies:
    -      arch: 3.0.0
    -
       '@xmldom/xmldom@0.8.10': {}
     
       '@xtuc/ieee754@1.2.0': {}
    @@ -18859,8 +18378,6 @@ snapshots:
       aproba@1.2.0:
         optional: true
     
    -  arch@3.0.0: {}
    -
       archiver-utils@5.0.2:
         dependencies:
           glob: 10.4.5
    @@ -19144,17 +18661,6 @@ snapshots:
     
       big.js@5.2.2: {}
     
    -  bin-version-check@5.1.0:
    -    dependencies:
    -      bin-version: 6.0.0
    -      semver: 7.7.2
    -      semver-truncate: 3.0.0
    -
    -  bin-version@6.0.0:
    -    dependencies:
    -      execa: 5.1.1
    -      find-versions: 5.1.0
    -
       binary-extensions@2.3.0: {}
     
       bindings@1.5.0:
    @@ -19356,18 +18862,6 @@ snapshots:
     
       cacheable-lookup@5.0.4: {}
     
    -  cacheable-lookup@7.0.0: {}
    -
    -  cacheable-request@10.2.14:
    -    dependencies:
    -      '@types/http-cache-semantics': 4.0.4
    -      get-stream: 6.0.1
    -      http-cache-semantics: 4.1.1
    -      keyv: 4.5.4
    -      mimic-response: 4.0.0
    -      normalize-url: 8.0.1
    -      responselike: 3.0.0
    -
       cacheable-request@7.0.4:
         dependencies:
           clone-response: 1.0.3
    @@ -19759,8 +19253,6 @@ snapshots:
     
       commander@6.2.0: {}
     
    -  commander@6.2.1: {}
    -
       commander@7.2.0: {}
     
       commander@8.3.0: {}
    @@ -20507,8 +19999,6 @@ snapshots:
         dependencies:
           clone: 1.0.4
     
    -  defaults@3.0.0: {}
    -
       defer-to-connect@2.0.1: {}
     
       define-data-property@1.1.4:
    @@ -21227,18 +20717,6 @@ snapshots:
           signal-exit: 3.0.7
           strip-eof: 1.0.0
     
    -  execa@5.1.1:
    -    dependencies:
    -      cross-spawn: 7.0.6
    -      get-stream: 6.0.1
    -      human-signals: 2.1.0
    -      is-stream: 2.0.1
    -      merge-stream: 2.0.0
    -      npm-run-path: 4.0.1
    -      onetime: 5.1.2
    -      signal-exit: 3.0.7
    -      strip-final-newline: 2.0.0
    -
       exif-parser@0.1.12: {}
     
       exit@0.1.2: {}
    @@ -21433,13 +20911,6 @@ snapshots:
           strtok3: 6.3.0
           token-types: 4.2.1
     
    -  file-type@19.6.0:
    -    dependencies:
    -      get-stream: 9.0.1
    -      strtok3: 9.1.1
    -      token-types: 6.0.0
    -      uint8array-extras: 1.4.0
    -
       file-type@20.5.0:
         dependencies:
           '@tokenizer/inflate': 0.2.7
    @@ -21459,18 +20930,12 @@ snapshots:
     
       filename-reserved-regex@2.0.0: {}
     
    -  filename-reserved-regex@3.0.0: {}
    -
       filenamify@4.3.0:
         dependencies:
           filename-reserved-regex: 2.0.0
           strip-outer: 1.0.1
           trim-repeated: 1.0.0
     
    -  filenamify@6.0.0:
    -    dependencies:
    -      filename-reserved-regex: 3.0.0
    -
       fill-range@7.1.1:
         dependencies:
           to-regex-range: 5.0.1
    @@ -21513,10 +20978,6 @@ snapshots:
           locate-path: 6.0.0
           path-exists: 4.0.0
     
    -  find-versions@5.1.0:
    -    dependencies:
    -      semver-regex: 4.0.5
    -
       flat-cache@4.0.1:
         dependencies:
           flatted: 3.3.3
    @@ -21583,8 +21044,6 @@ snapshots:
     
       form-data-encoder@1.7.2: {}
     
    -  form-data-encoder@2.1.4: {}
    -
       form-data@3.0.3:
         dependencies:
           asynckit: 0.4.0
    @@ -21806,13 +21265,6 @@ snapshots:
         dependencies:
           pump: 3.0.2
     
    -  get-stream@6.0.1: {}
    -
    -  get-stream@9.0.1:
    -    dependencies:
    -      '@sec-ant/readable-stream': 0.4.1
    -      is-stream: 4.0.1
    -
       get-symbol-description@1.1.0:
         dependencies:
           call-bound: 1.0.4
    @@ -21975,20 +21427,6 @@ snapshots:
           p-cancelable: 2.1.1
           responselike: 2.0.1
     
    -  got@13.0.0:
    -    dependencies:
    -      '@sindresorhus/is': 5.6.0
    -      '@szmarczak/http-timer': 5.0.1
    -      cacheable-lookup: 7.0.0
    -      cacheable-request: 10.2.14
    -      decompress-response: 6.0.0
    -      form-data-encoder: 2.1.4
    -      get-stream: 6.0.1
    -      http2-wrapper: 2.2.1
    -      lowercase-keys: 3.0.0
    -      p-cancelable: 3.0.0
    -      responselike: 3.0.0
    -
       graceful-fs@4.2.11: {}
     
       grapheme-splitter@1.0.4: {}
    @@ -22232,11 +21670,6 @@ snapshots:
           quick-lru: 5.1.1
           resolve-alpn: 1.2.1
     
    -  http2-wrapper@2.2.1:
    -    dependencies:
    -      quick-lru: 5.1.1
    -      resolve-alpn: 1.2.1
    -
       https-proxy-agent@5.0.1:
         dependencies:
           agent-base: 6.0.2
    @@ -22251,8 +21684,6 @@ snapshots:
         transitivePeerDependencies:
           - supports-color
     
    -  human-signals@2.1.0: {}
    -
       humanize-ms@1.2.1:
         dependencies:
           ms: 2.1.3
    @@ -22359,10 +21790,6 @@ snapshots:
     
       ini@5.0.0: {}
     
    -  inspect-with-kind@1.0.5:
    -    dependencies:
    -      kind-of: 6.0.3
    -
       internal-slot@1.1.0:
         dependencies:
           es-errors: 1.3.0
    @@ -22563,8 +21990,6 @@ snapshots:
     
       is-stream@2.0.1: {}
     
    -  is-stream@4.0.1: {}
    -
       is-string@1.1.1:
         dependencies:
           call-bound: 1.0.4
    @@ -23411,8 +22836,6 @@ snapshots:
     
       lowercase-keys@2.0.0: {}
     
    -  lowercase-keys@3.0.0: {}
    -
       lru-cache@10.4.3: {}
     
       lru-cache@5.1.1:
    @@ -23810,8 +23233,6 @@ snapshots:
     
       mimic-response@3.1.0: {}
     
    -  mimic-response@4.0.0: {}
    -
       mind-elixir@4.6.0: {}
     
       mini-css-extract-plugin@2.4.7(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)):
    @@ -24192,8 +23613,6 @@ snapshots:
     
       normalize-url@6.1.0: {}
     
    -  normalize-url@8.0.1: {}
    -
       normalize.css@8.0.1: {}
     
       npm-package-arg@11.0.1:
    @@ -24447,11 +23866,10 @@ snapshots:
           '@oxc-resolver/binding-wasm32-wasi': 5.3.0
           '@oxc-resolver/binding-win32-arm64-msvc': 5.3.0
           '@oxc-resolver/binding-win32-x64-msvc': 5.3.0
    +    optional: true
     
       p-cancelable@2.1.1: {}
     
    -  p-cancelable@3.0.0: {}
    -
       p-defer@1.0.0: {}
     
       p-finally@1.0.0: {}
    @@ -24654,8 +24072,6 @@ snapshots:
     
       peek-readable@4.1.0: {}
     
    -  peek-readable@5.4.2: {}
    -
       peek-readable@7.0.0: {}
     
       pend@1.2.0: {}
    @@ -24685,10 +24101,6 @@ snapshots:
     
       pirates@4.0.7: {}
     
    -  piscina@4.9.2:
    -    optionalDependencies:
    -      '@napi-rs/nice': 1.0.1
    -
       pixelmatch@5.3.0:
         dependencies:
           pngjs: 6.0.0
    @@ -25731,10 +25143,6 @@ snapshots:
         dependencies:
           lowercase-keys: 2.0.0
     
    -  responselike@3.0.0:
    -    dependencies:
    -      lowercase-keys: 3.0.0
    -
       resq@1.11.0:
         dependencies:
           fast-deep-equal: 2.0.1
    @@ -26065,10 +25473,6 @@ snapshots:
     
       secure-compare@3.0.1: {}
     
    -  seek-bzip@2.0.0:
    -    dependencies:
    -      commander: 6.2.1
    -
       select-hose@2.0.0: {}
     
       selfsigned@2.4.1:
    @@ -26079,12 +25483,6 @@ snapshots:
       semver-compare@1.0.0:
         optional: true
     
    -  semver-regex@4.0.5: {}
    -
    -  semver-truncate@3.0.0:
    -    dependencies:
    -      semver: 7.7.2
    -
       semver@5.7.2: {}
     
       semver@6.3.1: {}
    @@ -26556,15 +25954,8 @@ snapshots:
     
       strip-bom@5.0.0: {}
     
    -  strip-dirs@3.0.0:
    -    dependencies:
    -      inspect-with-kind: 1.0.5
    -      is-plain-obj: 1.1.0
    -
       strip-eof@1.0.0: {}
     
    -  strip-final-newline@2.0.0: {}
    -
       strip-json-comments@2.0.1: {}
     
       strip-json-comments@3.1.1: {}
    @@ -26587,11 +25978,6 @@ snapshots:
           '@tokenizer/token': 0.3.0
           peek-readable: 4.1.0
     
    -  strtok3@9.1.1:
    -    dependencies:
    -      '@tokenizer/token': 0.3.0
    -      peek-readable: 5.4.2
    -
       style-loader@2.0.0(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)):
         dependencies:
           loader-utils: 2.0.4
    @@ -26848,12 +26234,6 @@ snapshots:
           express: 4.21.2
           swagger-ui-dist: 5.21.0
     
    -  swc-loader@0.2.6(@swc/core@1.11.29(@swc/helpers@0.5.17))(webpack@5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)):
    -    dependencies:
    -      '@swc/core': 1.11.29(@swc/helpers@0.5.17)
    -      '@swc/counter': 0.1.3
    -      webpack: 5.99.9(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.4)
    -
       symbol-tree@3.2.4: {}
     
       sync-child-process@1.0.2:
    @@ -27018,8 +26398,6 @@ snapshots:
         dependencies:
           readable-stream: 3.6.2
     
    -  through@2.3.8: {}
    -
       thunky@1.1.0: {}
     
       time2fa@1.4.2: {}
    @@ -27327,11 +26705,6 @@ snapshots:
           has-symbols: 1.1.0
           which-boxed-primitive: 1.1.1
     
    -  unbzip2-stream@1.4.3:
    -    dependencies:
    -      buffer: 5.7.1
    -      through: 2.3.8
    -
       undici-types@6.19.8: {}
     
       undici-types@6.21.0: {}
    diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
    index eb7c37832..f7e733b59 100644
    --- a/pnpm-workspace.yaml
    +++ b/pnpm-workspace.yaml
    @@ -8,7 +8,6 @@ ignoredBuiltDependencies:
     onlyBuiltDependencies:
     - '@parcel/watcher'
     - '@scarf/scarf'
    -- '@swc/core'
     - bufferutil
     - core-js-pure
     - esbuild
    diff --git a/tsconfig.base.json b/tsconfig.base.json
    index 87eba8adc..76d2e73fc 100644
    --- a/tsconfig.base.json
    +++ b/tsconfig.base.json
    @@ -18,7 +18,7 @@
         "strict": true,
         "target": "es2022",
         "customConditions": ["development"],
    -    "verbatimModuleSyntax": true,
    +    "verbatimModuleSyntax": false,  // TODO: Re-enable it when migrating back to ESM.
         "resolveJsonModule": true,
         "downlevelIteration": true,
         "esModuleInterop": true,