Merge pull request #225 from aaronsb/chore/217-lint-debt

chore: adopt dev-dependency bump + clear lint debt (supersedes #217)
This commit is contained in:
Aaron Bockelie 2026-06-09 11:10:03 -05:00 committed by GitHub
commit 882a10c332
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 861 additions and 823 deletions

1564
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -29,19 +29,19 @@
"@eslint/js": "^9.39.2",
"@types/jest": "^30.0.0",
"@types/minimatch": "^5.1.2",
"@types/node": "^25.3.0",
"@typescript-eslint/utils": "^8.56.0",
"esbuild": "^0.27.3",
"@types/node": "^25.9.2",
"@typescript-eslint/utils": "^8.60.1",
"esbuild": "^0.28.0",
"eslint": "^9.39.2",
"eslint-plugin-obsidianmd": "^0.3.0",
"globals": "^17.3.0",
"jest": "^30.2.0",
"jiti": "^2.6.1",
"obsidian": "^1.12.2",
"ts-jest": "^29.1.1",
"globals": "^17.6.0",
"jest": "^30.4.2",
"jiti": "^2.7.0",
"obsidian": "^1.13.0",
"ts-jest": "^29.4.11",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.60.1"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.29.0",

View file

@ -87,9 +87,15 @@ function formatDataviewTable(headers: string[], rows: DataviewValue[]): string {
} else if (row !== null && typeof row === 'object') {
val = row[headers[i]];
}
const display = val === null || val === undefined
? ''
: typeof val === 'object' ? JSON.stringify(val) : String(val as string | number | boolean);
let display: string;
if (val === null || val === undefined) {
display = '';
} else if (typeof val === 'object') {
display = JSON.stringify(val);
} else {
const primitive = val as string | number | boolean | bigint | symbol;
display = String(primitive);
}
return truncate(display, 30);
});
lines.push('| ' + cells.join(' | ') + (hasMore ? ' | ...' : '') + ' |');
@ -327,7 +333,8 @@ export function formatBasesRead(response: BasesReadResponse): string {
} else if (typeof value === 'object') {
displayValue = JSON.stringify(value);
} else {
displayValue = String(value as string | number | boolean);
const primitive = value as string | number | boolean | bigint | symbol;
displayValue = String(primitive);
}
lines.push(property(key, truncate(displayValue, 50), 0));
});

View file

@ -226,7 +226,8 @@ export function formatFileRead(response: FileReadResponse): string {
displayValue = JSON.stringify(value).substring(0, 50);
} else {
// Primitives (string, number, boolean, bigint, symbol) are safe to stringify
displayValue = String(value as string | number | boolean | bigint | symbol).substring(0, 50);
const primitive = value as string | number | boolean | bigint | symbol;
displayValue = String(primitive).substring(0, 50);
}
lines.push(property(key, displayValue, 0));
});

View file

@ -582,7 +582,17 @@ class MCPSettingTab extends PluginSettingTab {
this.plugin = plugin;
}
// Obsidian's settings entry point. PluginSettingTab.display() is deprecated
// since 1.13.0 in favor of the declarative getSettingDefinitions() API, but
// display() remains the supported fallback for plugins that still target
// older Obsidian versions. Keep it as a thin wrapper; the imperative render
// lives in render(), which internal refreshes call directly so they don't
// reference the deprecated method. Declarative migration tracked in #224.
display(): void {
this.render();
}
private render(): void {
const {containerEl} = this;
containerEl.empty();
@ -692,7 +702,7 @@ class MCPSettingTab extends PluginSettingTab {
}
// Update the status display
this.display();
this.render();
}));
const portSetting = new Setting(containerEl)
@ -805,7 +815,7 @@ class MCPSettingTab extends PluginSettingTab {
this.plugin.settings.customBindHost = '';
}
await this.plugin.saveSettings();
this.display();
this.render();
await this.restartIfRunning('bind address');
}));
@ -839,7 +849,7 @@ class MCPSettingTab extends PluginSettingTab {
this.plugin.settings.bindMode = normalized.mode;
this.plugin.settings.customBindHost = normalized.customHost;
await this.plugin.saveSettings();
this.display();
this.render();
await this.restartIfRunning('bind address');
})();
}));
@ -876,7 +886,7 @@ class MCPSettingTab extends PluginSettingTab {
await this.plugin.saveSettings();
// Show/hide HTTPS settings and update HTTP toggle state
this.display();
this.render();
// Restart server if running
if (this.plugin.mcpServer?.isServerRunning()) {
@ -919,7 +929,7 @@ class MCPSettingTab extends PluginSettingTab {
this.plugin.settings.certificateConfig.autoGenerate = value;
await this.plugin.saveSettings();
// Refresh the display to update the description
this.display();
this.render();
}));
new Setting(containerEl)
@ -932,7 +942,7 @@ class MCPSettingTab extends PluginSettingTab {
this.plugin.settings.certificateConfig.certPath = value || undefined;
await this.plugin.saveSettings();
// Refresh display to update configuration examples
this.display();
this.render();
}));
new Setting(containerEl)
@ -1020,6 +1030,11 @@ class MCPSettingTab extends PluginSettingTab {
.addButton(button => button
.setButtonText('Regenerate')
.setTooltip('Generate a new API key')
// setDestructive() (the non-deprecated replacement) requires Obsidian
// 1.13.0, but our manifest minAppVersion is 1.6.6, so it's not available
// to all supported users. Keep setWarning() until minAppVersion is raised
// (tracked with the 1.13.0 API adoption in #224).
// eslint-disable-next-line @typescript-eslint/no-deprecated
.setWarning()
.onClick(() => {
new ConfirmationModal(
@ -1029,7 +1044,7 @@ class MCPSettingTab extends PluginSettingTab {
this.plugin.settings.apiKey = this.plugin.generateApiKey();
await this.plugin.saveSettings();
new Notice('API key regenerated. Update your mcp clients with the new key.');
this.display();
this.render();
}
).open();
}));
@ -1064,7 +1079,7 @@ class MCPSettingTab extends PluginSettingTab {
}
// Refresh display to update examples
this.display();
this.render();
}));
}
@ -1090,7 +1105,7 @@ class MCPSettingTab extends PluginSettingTab {
}
// Refresh display to update examples
this.display();
this.render();
}));
// Path Exclusions Setting
@ -1116,7 +1131,7 @@ class MCPSettingTab extends PluginSettingTab {
}
// Refresh display to show/hide file management options
this.display();
this.render();
}));
// Show context menu toggle if path exclusions are enabled
@ -1290,7 +1305,7 @@ class MCPSettingTab extends PluginSettingTab {
// Force reload to ensure fresh state
await this.plugin.ignoreManager!.forceReload();
new Notice('📄 Default .mcpignore template created');
this.display(); // Refresh to update status
this.render(); // Refresh to update status
} catch (error) {
Debug.log('Failed to create .mcpignore template:', error);
new Notice('❌ Failed to create template');
@ -1307,7 +1322,7 @@ class MCPSettingTab extends PluginSettingTab {
try {
await this.plugin.ignoreManager!.forceReload();
new Notice('🔄 Exclusion patterns reloaded');
this.display(); // Refresh to update status
this.render(); // Refresh to update status
} catch (error) {
Debug.log('Failed to reload patterns:', error);
new Notice('❌ Failed to reload patterns');
@ -1412,7 +1427,7 @@ class MCPSettingTab extends PluginSettingTab {
visibility[`${operation}.${action}`] = value;
}
await this.plugin.saveSettings();
this.display(); // Re-render for updated states
this.render(); // Re-render for updated states
});
});
@ -1448,7 +1463,7 @@ class MCPSettingTab extends PluginSettingTab {
}
await this.plugin.saveSettings();
this.display(); // Re-render for parent state update
this.render(); // Re-render for parent state update
}));
}
}
@ -1763,7 +1778,7 @@ class MCPSettingTab extends PluginSettingTab {
refreshConnectionStatus(): void {
// Simply refresh the entire settings display to ensure accurate data
// This is more reliable than trying to manually update DOM elements
this.display();
this.render();
}
private updatePortApplyButton(setting: Setting, hasChanges: boolean, pendingPort: number): void {
@ -1784,7 +1799,7 @@ class MCPSettingTab extends PluginSettingTab {
const info = this.plugin.getMCPServerInfo();
// Update connection status grid
const connectionEl = document.querySelector('.mcp-status-grid');
const connectionEl = activeDocument.querySelector('.mcp-status-grid');
if (connectionEl) {
const connectionItems = connectionEl.querySelectorAll('div');
for (let i = 0; i < connectionItems.length; i++) {
@ -1807,7 +1822,7 @@ class MCPSettingTab extends PluginSettingTab {
// Update Claude Code connection section with proper auth handling.
// Rebuild via the shared renderer so the live view matches the initial
// render exactly (including the JSON-config + warning auth path).
const protocolSection = document.querySelector('.protocol-command-example');
const protocolSection = activeDocument.querySelector('.protocol-command-example');
if (protocolSection instanceof HTMLElement && info) {
// Get correct protocol and port based on HTTPS setting
const protocol = this.plugin.settings.httpsEnabled ? 'https' : 'http';
@ -1818,7 +1833,7 @@ class MCPSettingTab extends PluginSettingTab {
}
// Update any other dynamic content areas that need live updates
const statusElements = document.querySelectorAll('[data-live-update]');
const statusElements = activeDocument.querySelectorAll('[data-live-update]');
for (let i = 0; i < statusElements.length; i++) {
const el = statusElements[i];
const updateType = el.getAttribute('data-live-update');

View file

@ -1,7 +1,7 @@
import express from 'express';
import cors from 'cors';
import { App, Notice } from 'obsidian';
import { createServer as createHttpServer, Server, IncomingMessage, ServerResponse } from 'http';
import { createServer as createHttpServer, Server } from 'http';
import { Server as HttpsServer } from 'https';
import { Server as MCPServer } from '@modelcontextprotocol/sdk/server/index.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
@ -571,9 +571,9 @@ export class MCPHttpServer {
// Handle the request using the transport
await transport.handleRequest(
req as unknown as IncomingMessage,
res as unknown as ServerResponse,
request as unknown
req,
res,
request
);
Debug.log('📤 MCP Response sent via transport');

View file

@ -143,10 +143,10 @@ export class VaultSecurityManager {
};
if (operation.path) {
result.path = operation.path as ValidatedPath;
result.path = operation.path;
}
if (operation.targetPath) {
result.targetPath = operation.targetPath as ValidatedPath;
result.targetPath = operation.targetPath;
}
return result as ValidatedOperation;

View file

@ -436,7 +436,8 @@ export class BasesAPI {
if (value == null) return '';
if (typeof value === 'object') return JSON.stringify(value);
// At this point value is a primitive (string, number, boolean, bigint, symbol)
return String(value as string | number | boolean | bigint | symbol);
const primitive = value as string | number | boolean | bigint | symbol;
return String(primitive);
}
private escapeCSV(value: unknown): string {

View file

@ -87,7 +87,7 @@ export class ConnectionPool extends EventEmitter {
} else if (typeof response.error === 'string') {
reject(new Error(response.error));
} else if (response.error && typeof response.error === 'object' && 'message' in response.error) {
reject(new Error(String((response.error as { message: unknown }).message)));
reject(new Error(String((response.error).message)));
} else {
reject(new Error(JSON.stringify(response.error)));
}

View file

@ -81,7 +81,8 @@ export function ensureStringContent(content: unknown, context?: string): string
}
// Fallback: convert primitives (number, boolean, bigint, symbol) to string
return String(content as string | number | boolean | bigint | symbol);
const primitive = content as string | number | boolean | bigint | symbol;
return String(primitive);
} catch (error) {
Debug.warn(`Content conversion failed${context ? ` in ${context}` : ''}:`, {

View file

@ -115,7 +115,7 @@ export async function readFileWithFragments(
// Image / binary: passthrough unchanged
if (isImageFile(fileResponse)) {
return fileResponse as FileReadResult;
return fileResponse;
}
// Extract verbatim content + metadata (metadata WITHOUT a copy of the body)
@ -129,7 +129,7 @@ export async function readFileWithFragments(
} else if (fileResponse && typeof fileResponse === 'object' && 'content' in fileResponse) {
const fc = (fileResponse as { content: unknown }).content;
if (typeof fc !== 'string') {
return fileResponse as FileReadResult; // image/binary structured
return fileResponse; // image/binary structured
}
fileContent = fc;
// Strip the body so it is not embedded twice in the envelope (ADR-203 §3)
@ -140,7 +140,7 @@ export async function readFileWithFragments(
frontmatter = fm;
tags = tg;
} else {
return fileResponse as FileReadResult;
return fileResponse;
}
const totalChars = fileContent.length;

View file

@ -112,7 +112,7 @@ async function resizeImageWithCanvas(
// Check if resizing is needed
if (originalWidth <= maxDimension && originalHeight <= maxDimension) {
// No resizing needed, but convert to JPEG with quality
const canvas = document.createElement('canvas');
const canvas = activeDocument.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
@ -165,7 +165,7 @@ async function resizeImageWithCanvas(
}
// Create canvas for resizing
const canvas = document.createElement('canvas');
const canvas = activeDocument.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {

View file

@ -187,7 +187,8 @@ export class FileSizeValidator implements Validator {
contentStr = JSON.stringify(content);
} else {
// Primitives (number, boolean, bigint, symbol) are safe to stringify
contentStr = String(content as number | boolean | bigint | symbol);
const primitive = content as string | number | boolean | bigint | symbol;
contentStr = String(primitive);
}
const sizeInBytes = Buffer.byteLength(contentStr, 'utf8');