diff --git a/src/js/utils/compress.ts b/src/js/utils/compress.ts
index 47a15bb..d933406 100644
--- a/src/js/utils/compress.ts
+++ b/src/js/utils/compress.ts
@@ -69,7 +69,10 @@ export async function performCondenseCompression(
scrub: {
metadata: customSettings?.removeMetadata ?? preset.scrub.metadata,
thumbnails: customSettings?.removeThumbnails ?? preset.scrub.thumbnails,
- xmlMetadata: (preset.scrub as any).xmlMetadata ?? false,
+ xmlMetadata:
+ ('xmlMetadata' in preset.scrub
+ ? (preset.scrub as { xmlMetadata?: boolean }).xmlMetadata
+ : undefined) ?? false,
},
subsetFonts: customSettings?.subsetFonts ?? preset.subsetFonts,
save: {
@@ -95,8 +98,11 @@ export async function performCondenseCompression(
try {
const result = await pymupdf.compressPdf(fileBlob, fallbackOptions);
return { ...result, usedFallback: true };
- } catch (fallbackError: any) {
- const msg = fallbackError?.message || String(fallbackError);
+ } catch (fallbackError: unknown) {
+ const msg =
+ fallbackError instanceof Error
+ ? fallbackError.message
+ : String(fallbackError);
throw new Error(`PDF compression failed: ${msg}`, {
cause: fallbackError,
});
diff --git a/src/js/utils/cpdf-helper.ts b/src/js/utils/cpdf-helper.ts
index a1a004b..32a59aa 100644
--- a/src/js/utils/cpdf-helper.ts
+++ b/src/js/utils/cpdf-helper.ts
@@ -1,4 +1,5 @@
import { WasmProvider } from './wasm-provider';
+import type { WindowWithCoherentPdf, CpdfInstance } from '@/types';
let cpdfLoaded = false;
let cpdfLoadPromise: Promise
| null = null;
@@ -31,7 +32,7 @@ export async function isCpdfLoaded(): Promise {
}
cpdfLoadPromise = new Promise((resolve, reject) => {
- if (typeof (window as any).coherentpdf !== 'undefined') {
+ if (typeof (window as WindowWithCoherentPdf).coherentpdf !== 'undefined') {
cpdfLoaded = true;
resolve();
return;
@@ -53,7 +54,7 @@ export async function isCpdfLoaded(): Promise {
return cpdfLoadPromise;
}
-export async function getCpdf(): Promise {
+export async function getCpdf(): Promise {
await isCpdfLoaded();
- return (window as any).coherentpdf;
+ return (window as WindowWithCoherentPdf).coherentpdf as CpdfInstance;
}
diff --git a/src/js/utils/disabled-tools.ts b/src/js/utils/disabled-tools.ts
index dfbc704..876e635 100644
--- a/src/js/utils/disabled-tools.ts
+++ b/src/js/utils/disabled-tools.ts
@@ -27,7 +27,9 @@ export async function loadRuntimeConfig(): Promise {
(c): c is string => typeof c === 'string'
);
}
- } catch {}
+ } catch {
+ console.error('[LOAD_RUNTIME_CONFIG] Failed to load runtime configuration');
+ }
}
export function isToolDisabled(toolId: string): boolean {
diff --git a/src/js/utils/ghostscript-dynamic-loader.ts b/src/js/utils/ghostscript-dynamic-loader.ts
index e43e3fa..4a84375 100644
--- a/src/js/utils/ghostscript-dynamic-loader.ts
+++ b/src/js/utils/ghostscript-dynamic-loader.ts
@@ -1,7 +1,11 @@
import { WasmProvider } from './wasm-provider.js';
+import type {
+ GlobalScopeWithGhostscript,
+ GhostscriptDynamicInstance,
+} from '@/types';
-let cachedGS: any = null;
-let loadPromise: Promise | null = null;
+let cachedGS: GhostscriptInterface | null = null;
+let loadPromise: Promise | null = null;
export interface GhostscriptInterface {
convertToPDFA(pdfBuffer: ArrayBuffer, profile: string): Promise;
@@ -32,16 +36,20 @@ export async function loadGhostscript(): Promise {
await loadScript(wrapperUrl);
- const globalScope =
- typeof globalThis !== 'undefined' ? globalThis : window;
+ const globalScope = (
+ typeof globalThis !== 'undefined' ? globalThis : window
+ ) as typeof globalThis & GlobalScopeWithGhostscript;
- if (typeof (globalScope as any).loadGS === 'function') {
- cachedGS = await (globalScope as any).loadGS({
+ if (typeof globalScope.loadGS === 'function') {
+ const instance = await globalScope.loadGS({
baseUrl: normalizedUrl,
});
- } else if (typeof (globalScope as any).GhostscriptWASM === 'function') {
- cachedGS = new (globalScope as any).GhostscriptWASM(normalizedUrl);
- await cachedGS.init?.();
+ cachedGS = instance as unknown as GhostscriptInterface;
+ } else if (typeof globalScope.GhostscriptWASM === 'function') {
+ const instance: GhostscriptDynamicInstance =
+ new globalScope.GhostscriptWASM(normalizedUrl);
+ await instance.init?.();
+ cachedGS = instance as unknown as GhostscriptInterface;
} else {
throw new Error(
'Ghostscript wrapper did not expose expected interface. Expected loadGS() or GhostscriptWASM class.'
@@ -49,10 +57,11 @@ export async function loadGhostscript(): Promise {
}
return cachedGS;
- } catch (error: any) {
+ } catch (error: unknown) {
loadPromise = null;
+ const msg = error instanceof Error ? error.message : String(error);
throw new Error(
- `Failed to load Ghostscript from ${normalizedUrl}: ${error.message}`,
+ `Failed to load Ghostscript from ${normalizedUrl}: ${msg}`,
{ cause: error }
);
}
diff --git a/src/js/utils/helpers.ts b/src/js/utils/helpers.ts
index 7ec12f2..5056ac7 100644
--- a/src/js/utils/helpers.ts
+++ b/src/js/utils/helpers.ts
@@ -1,8 +1,10 @@
import createModule from '@neslinesli93/qpdf-wasm';
+import type { QpdfInstanceExtended } from '@/types';
import { showLoader, hideLoader, showAlert } from '../ui.js';
import { createIcons } from 'lucide';
import { state, resetState } from '../state.js';
import * as pdfjsLib from 'pdfjs-dist';
+import type { DocumentInitParameters } from 'pdfjs-dist/types/src/display/api';
const STANDARD_SIZES = {
A4: { width: 595.28, height: 841.89 },
@@ -13,7 +15,7 @@ const STANDARD_SIZES = {
A5: { width: 419.53, height: 595.28 },
};
-export function getStandardPageName(width: any, height: any) {
+export function getStandardPageName(width: number, height: number) {
const tolerance = 1; // Allow for minor floating point variations
for (const [name, size] of Object.entries(STANDARD_SIZES)) {
if (
@@ -28,7 +30,7 @@ export function getStandardPageName(width: any, height: any) {
return 'Custom';
}
-export function convertPoints(points: any, unit: any) {
+export function convertPoints(points: number, unit: string) {
let result: number;
switch (unit) {
case 'in':
@@ -59,7 +61,7 @@ export function hexToRgb(hex: string): { r: number; g: number; b: number } {
: { r: 0, g: 0, b: 0 };
}
-export const formatBytes = (bytes: any, decimals = 1) => {
+export const formatBytes = (bytes: number, decimals = 1) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
@@ -79,10 +81,12 @@ export const downloadFile = (blob: Blob, filename: string): void => {
URL.revokeObjectURL(url);
};
-export const readFileAsArrayBuffer = (file: any) => {
+export const readFileAsArrayBuffer = (
+ file: Blob
+): Promise => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
- reader.onload = () => resolve(reader.result);
+ reader.onload = () => resolve(reader.result as ArrayBuffer | null);
reader.onerror = (error) => reject(error);
reader.readAsArrayBuffer(file);
});
@@ -139,7 +143,7 @@ export function parsePageRanges(
* @param {string} isoDateString - The ISO 8601 date string.
* @returns {string} A localized date and time string, or the original string if parsing fails.
*/
-export function formatIsoDate(isoDateString) {
+export function formatIsoDate(isoDateString: string) {
if (!isoDateString || typeof isoDateString !== 'string') {
return isoDateString; // Return original value if it's not a valid string
}
@@ -156,20 +160,20 @@ export function formatIsoDate(isoDateString) {
}
}
-let qpdfInstance: any = null;
+let qpdfInstance: QpdfInstanceExtended | null = null;
/**
* Initialize qpdf-wasm singleton.
* Subsequent calls return the same instance.
*/
-export async function initializeQpdf() {
+export async function initializeQpdf(): Promise {
if (qpdfInstance) return qpdfInstance;
showLoader('Initializing PDF engine...');
try {
- qpdfInstance = await createModule({
+ qpdfInstance = (await createModule({
locateFile: () => import.meta.env.BASE_URL + 'qpdf.wasm',
- });
+ })) as unknown as QpdfInstanceExtended;
} catch (error) {
console.error('Failed to initialize qpdf-wasm:', error);
showAlert(
@@ -267,24 +271,19 @@ export function resetAndReloadTool(preResetCallback?: () => void) {
* @param src The source to load (url string, typed array, or parameters object)
* @returns The PDF loading task
*/
-export function getPDFDocument(src: any) {
- let params = src;
+export function getPDFDocument(
+ src: string | Uint8Array | ArrayBuffer | DocumentInitParameters
+) {
+ let params: DocumentInitParameters;
- // Handle different input types similar to how getDocument handles them,
- // but we ensure we have an object to attach wasmUrl to.
if (typeof src === 'string') {
params = { url: src };
} else if (src instanceof Uint8Array || src instanceof ArrayBuffer) {
params = { data: src };
+ } else {
+ params = src;
}
- // Ensure params is an object
- if (typeof params !== 'object' || params === null) {
- params = {};
- }
-
- // Add wasmUrl pointing to our public/wasm directory
- // This is required for PDF.js v5+ to load OpenJPEG for certain images
return pdfjsLib.getDocument({
...params,
wasmUrl: import.meta.env.BASE_URL + 'pdfjs-viewer/wasm/',
@@ -413,7 +412,7 @@ export function formatRawDate(raw: string): string {
year,
hoursStr,
minsStr,
- secsStr,
+ _secsStr,
timezone,
] = match;
@@ -455,8 +454,8 @@ export function formatRawDate(raw: string): string {
return `${fullDay}, ${fullMonth} ${dom}, ${year} at ${hours}:${minsStr} ${ampm} (${formattedTz})`;
}
- } catch (e) {
- // Fallback to raw string if parsing fails
+ } catch {
+ console.error('Error parsing date string:', raw);
}
return raw;
}
diff --git a/src/js/utils/libreoffice-loader.ts b/src/js/utils/libreoffice-loader.ts
index 54b5b73..9936eb6 100644
--- a/src/js/utils/libreoffice-loader.ts
+++ b/src/js/utils/libreoffice-loader.ts
@@ -1,18 +1,19 @@
/**
* LibreOffice WASM Converter Wrapper
- *
+ *
* Uses @matbee/libreoffice-converter package for document conversion.
* Handles progress tracking and provides simpler API.
*/
import { WorkerBrowserConverter } from '@matbee/libreoffice-converter/browser';
+import type { InputFormat } from '@matbee/libreoffice-converter/browser';
const LIBREOFFICE_LOCAL_PATH = import.meta.env.BASE_URL + 'libreoffice-wasm/';
export interface LoadProgress {
- phase: 'loading' | 'initializing' | 'converting' | 'complete' | 'ready';
- percent: number;
- message: string;
+ phase: 'loading' | 'initializing' | 'converting' | 'complete' | 'ready';
+ percent: number;
+ message: string;
}
export type ProgressCallback = (progress: LoadProgress) => void;
@@ -21,140 +22,161 @@ export type ProgressCallback = (progress: LoadProgress) => void;
let converterInstance: LibreOfficeConverter | null = null;
export class LibreOfficeConverter {
- private converter: WorkerBrowserConverter | null = null;
- private initialized = false;
- private initializing = false;
- private basePath: string;
+ private converter: WorkerBrowserConverter | null = null;
+ private initialized = false;
+ private initializing = false;
+ private basePath: string;
- constructor(basePath?: string) {
- this.basePath = basePath || LIBREOFFICE_LOCAL_PATH;
+ constructor(basePath?: string) {
+ this.basePath = basePath || LIBREOFFICE_LOCAL_PATH;
+ }
+
+ async initialize(onProgress?: ProgressCallback): Promise {
+ if (this.initialized) return;
+
+ if (this.initializing) {
+ while (this.initializing) {
+ await new Promise((r) => setTimeout(r, 100));
+ }
+ return;
}
- async initialize(onProgress?: ProgressCallback): Promise {
- if (this.initialized) return;
+ this.initializing = true;
+ let progressCallback = onProgress; // Store original callback
- if (this.initializing) {
- while (this.initializing) {
- await new Promise(r => setTimeout(r, 100));
- }
- return;
- }
+ try {
+ progressCallback?.({
+ phase: 'loading',
+ percent: 0,
+ message: 'Loading conversion engine...',
+ });
-
- this.initializing = true;
- let progressCallback = onProgress; // Store original callback
-
- try {
- progressCallback?.({ phase: 'loading', percent: 0, message: 'Loading conversion engine...' });
-
- this.converter = new WorkerBrowserConverter({
- sofficeJs: `${this.basePath}soffice.js`,
- sofficeWasm: `${this.basePath}soffice.wasm.gz`,
- sofficeData: `${this.basePath}soffice.data.gz`,
- sofficeWorkerJs: `${this.basePath}soffice.worker.js`,
- browserWorkerJs: `${this.basePath}browser.worker.global.js`,
- verbose: false,
- onProgress: (info: { phase: string; percent: number; message: string }) => {
- if (progressCallback && !this.initialized) {
- const simplifiedMessage = `Loading conversion engine (${Math.round(info.percent)}%)...`;
- progressCallback({
- phase: info.phase as LoadProgress['phase'],
- percent: info.percent,
- message: simplifiedMessage
- });
- }
- },
- onReady: () => {
- console.log('[LibreOffice] Ready!');
- },
- onError: (error: Error) => {
- console.error('[LibreOffice] Error:', error);
- },
+ this.converter = new WorkerBrowserConverter({
+ sofficeJs: `${this.basePath}soffice.js`,
+ sofficeWasm: `${this.basePath}soffice.wasm.gz`,
+ sofficeData: `${this.basePath}soffice.data.gz`,
+ sofficeWorkerJs: `${this.basePath}soffice.worker.js`,
+ browserWorkerJs: `${this.basePath}browser.worker.global.js`,
+ verbose: false,
+ onProgress: (info: {
+ phase: string;
+ percent: number;
+ message: string;
+ }) => {
+ if (progressCallback && !this.initialized) {
+ const simplifiedMessage = `Loading conversion engine (${Math.round(info.percent)}%)...`;
+ progressCallback({
+ phase: info.phase as LoadProgress['phase'],
+ percent: info.percent,
+ message: simplifiedMessage,
});
+ }
+ },
+ onReady: () => {
+ console.log('[LibreOffice] Ready!');
+ },
+ onError: (error: Error) => {
+ console.error('[LibreOffice] Error:', error);
+ },
+ });
- await this.converter.initialize();
- this.initialized = true;
+ await this.converter.initialize();
+ this.initialized = true;
- // Call completion message
- progressCallback?.({ phase: 'ready', percent: 100, message: 'Conversion engine ready!' });
+ // Call completion message
+ progressCallback?.({
+ phase: 'ready',
+ percent: 100,
+ message: 'Conversion engine ready!',
+ });
- // Null out the callback to prevent any late-firing progress updates
- progressCallback = undefined;
- } finally {
- this.initializing = false;
- }
+ // Null out the callback to prevent any late-firing progress updates
+ progressCallback = undefined;
+ } finally {
+ this.initializing = false;
+ }
+ }
+
+ isReady(): boolean {
+ return this.initialized && this.converter !== null;
+ }
+
+ async convertToPdf(file: File): Promise {
+ if (!this.converter) {
+ throw new Error('Converter not initialized');
}
- isReady(): boolean {
- return this.initialized && this.converter !== null;
+ console.log(`[LibreOffice] Converting ${file.name} to PDF...`);
+ console.log(
+ `[LibreOffice] File type: ${file.type}, Size: ${file.size} bytes`
+ );
+
+ try {
+ console.log(`[LibreOffice] Reading file as ArrayBuffer...`);
+ const arrayBuffer = await file.arrayBuffer();
+ const uint8Array = new Uint8Array(arrayBuffer);
+ console.log(`[LibreOffice] File loaded, ${uint8Array.length} bytes`);
+
+ console.log(`[LibreOffice] Calling converter.convert() with buffer...`);
+ const startTime = Date.now();
+
+ // Detect input format - critical for CSV to apply import filters
+ const ext = file.name.split('.').pop()?.toLowerCase() || '';
+ console.log(`[LibreOffice] Detected format from extension: ${ext}`);
+
+ const result = await this.converter.convert(
+ uint8Array,
+ {
+ outputFormat: 'pdf',
+ inputFormat: ext as InputFormat,
+ },
+ file.name
+ );
+
+ const duration = Date.now() - startTime;
+ console.log(
+ `[LibreOffice] Conversion complete! Duration: ${duration}ms, Size: ${result.data.length} bytes`
+ );
+
+ // Create a copy to avoid SharedArrayBuffer type issues
+ const data = new Uint8Array(result.data);
+ return new Blob([data], { type: result.mimeType });
+ } catch (error) {
+ console.error(`[LibreOffice] Conversion FAILED for ${file.name}:`, error);
+ console.error(`[LibreOffice] Error details:`, {
+ message: error instanceof Error ? error.message : String(error),
+ stack: error instanceof Error ? error.stack : undefined,
+ });
+ throw error;
}
+ }
- async convertToPdf(file: File): Promise {
- if (!this.converter) {
- throw new Error('Converter not initialized');
- }
+ async wordToPdf(file: File): Promise {
+ return this.convertToPdf(file);
+ }
- console.log(`[LibreOffice] Converting ${file.name} to PDF...`);
- console.log(`[LibreOffice] File type: ${file.type}, Size: ${file.size} bytes`);
+ async pptToPdf(file: File): Promise {
+ return this.convertToPdf(file);
+ }
- try {
- console.log(`[LibreOffice] Reading file as ArrayBuffer...`);
- const arrayBuffer = await file.arrayBuffer();
- const uint8Array = new Uint8Array(arrayBuffer);
- console.log(`[LibreOffice] File loaded, ${uint8Array.length} bytes`);
+ async excelToPdf(file: File): Promise {
+ return this.convertToPdf(file);
+ }
- console.log(`[LibreOffice] Calling converter.convert() with buffer...`);
- const startTime = Date.now();
-
- // Detect input format - critical for CSV to apply import filters
- const ext = file.name.split('.').pop()?.toLowerCase() || '';
- console.log(`[LibreOffice] Detected format from extension: ${ext}`);
-
- const result = await this.converter.convert(uint8Array, {
- outputFormat: 'pdf',
- inputFormat: ext as any, // Explicitly specify format for CSV import filters
- }, file.name);
-
- const duration = Date.now() - startTime;
- console.log(`[LibreOffice] Conversion complete! Duration: ${duration}ms, Size: ${result.data.length} bytes`);
-
- // Create a copy to avoid SharedArrayBuffer type issues
- const data = new Uint8Array(result.data);
- return new Blob([data], { type: result.mimeType });
- } catch (error) {
- console.error(`[LibreOffice] Conversion FAILED for ${file.name}:`, error);
- console.error(`[LibreOffice] Error details:`, {
- message: error instanceof Error ? error.message : String(error),
- stack: error instanceof Error ? error.stack : undefined
- });
- throw error;
- }
- }
-
- async wordToPdf(file: File): Promise {
- return this.convertToPdf(file);
- }
-
- async pptToPdf(file: File): Promise {
- return this.convertToPdf(file);
- }
-
- async excelToPdf(file: File): Promise {
- return this.convertToPdf(file);
- }
-
- async destroy(): Promise {
- if (this.converter) {
- await this.converter.destroy();
- }
- this.converter = null;
- this.initialized = false;
+ async destroy(): Promise {
+ if (this.converter) {
+ await this.converter.destroy();
}
+ this.converter = null;
+ this.initialized = false;
+ }
}
-export function getLibreOfficeConverter(basePath?: string): LibreOfficeConverter {
- if (!converterInstance) {
- converterInstance = new LibreOfficeConverter(basePath);
- }
- return converterInstance;
+export function getLibreOfficeConverter(
+ basePath?: string
+): LibreOfficeConverter {
+ if (!converterInstance) {
+ converterInstance = new LibreOfficeConverter(basePath);
+ }
+ return converterInstance;
}
diff --git a/src/js/utils/markdown-editor.ts b/src/js/utils/markdown-editor.ts
index a1a450f..8fbbb25 100644
--- a/src/js/utils/markdown-editor.ts
+++ b/src/js/utils/markdown-editor.ts
@@ -29,8 +29,14 @@ import taskLists from 'markdown-it-task-lists';
import anchor from 'markdown-it-anchor';
import tocDoneRight from 'markdown-it-toc-done-right';
import { applyTranslations } from '../i18n/i18n';
-
-
+import type {
+ WindowWithLucide,
+ WindowWithI18next,
+ MarkdownItOptions,
+ MarkdownItToken,
+ MarkdownItRendererSelf,
+ MarkdownItRenderRule,
+} from '@/types';
// Register highlight.js languages
hljs.registerLanguage('javascript', javascript);
@@ -66,18 +72,7 @@ export interface MarkdownEditorOptions {
onBack?: () => void;
}
-export interface MarkdownItOptions {
- /** Enable HTML tags in source */
- html: boolean;
- /** Convert '\n' in paragraphs into
*/
- breaks: boolean;
- /** Autoconvert URL-like text to links */
- linkify: boolean;
- /** Enable some language-neutral replacement + quotes beautification */
- typographer: boolean;
- /** Highlight function for fenced code blocks */
- highlight?: (str: string, lang: string) => string;
-}
+export type { MarkdownItOptions } from '@/types';
const DEFAULT_MARKDOWN = `# Welcome to BentoPDF Markdown Editor
@@ -277,7 +272,7 @@ export class MarkdownEditor {
html: true,
breaks: false,
linkify: true,
- typographer: true
+ typographer: true,
};
constructor(container: HTMLElement, options: MarkdownEditorOptions) {
@@ -303,22 +298,36 @@ export class MarkdownEditor {
startOnLoad: false,
theme: 'default',
securityLevel: 'loose',
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
+ fontFamily:
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
});
this.mermaidInitialized = true;
}
}
private configureLinkRenderer(): void {
- // Override link renderer to add target="_blank" and rel="noopener"
- const defaultRender = this.md.renderer.rules.link_open ||
- ((tokens: any[], idx: number, options: any, _env: any, self: any) => self.renderToken(tokens, idx, options));
+ const existingRule = this.md.renderer.rules.link_open;
+ const defaultRender: MarkdownItRenderRule = existingRule
+ ? (existingRule as unknown as MarkdownItRenderRule)
+ : (
+ tokens: MarkdownItToken[],
+ idx: number,
+ options: MarkdownItOptions,
+ env: unknown,
+ self: MarkdownItRendererSelf
+ ) => self.renderToken(tokens, idx, options);
- this.md.renderer.rules.link_open = (tokens: any[], idx: number, options: any, env: any, self: any) => {
- const token = tokens[idx];
+ this.md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
+ const token = tokens[idx] as unknown as MarkdownItToken;
token.attrSet('target', '_blank');
token.attrSet('rel', 'noopener noreferrer');
- return defaultRender(tokens, idx, options, env, self);
+ return defaultRender(
+ tokens as unknown as MarkdownItToken[],
+ idx,
+ options as unknown as MarkdownItOptions,
+ env as unknown,
+ self as unknown as MarkdownItRendererSelf
+ );
};
}
@@ -417,12 +426,11 @@ export class MarkdownEditor {
this.applyI18n();
// Initialize Lucide icons
- if (typeof (window as any).lucide !== 'undefined') {
- (window as any).lucide.createIcons();
+ if (typeof (window as WindowWithLucide).lucide !== 'undefined') {
+ (window as WindowWithLucide).lucide?.createIcons();
}
}
-
private setupEventListeners(): void {
// Editor input
this.editor?.addEventListener('input', () => {
@@ -441,9 +449,13 @@ export class MarkdownEditor {
this.editor?.addEventListener('scroll', () => {
if (this.syncScroll && !this.isSyncing && this.editor && this.preview) {
this.isSyncing = true;
- const scrollPercentage = this.editor.scrollTop / (this.editor.scrollHeight - this.editor.clientHeight);
- this.preview.scrollTop = scrollPercentage * (this.preview.scrollHeight - this.preview.clientHeight);
- setTimeout(() => this.isSyncing = false, 10);
+ const scrollPercentage =
+ this.editor.scrollTop /
+ (this.editor.scrollHeight - this.editor.clientHeight);
+ this.preview.scrollTop =
+ scrollPercentage *
+ (this.preview.scrollHeight - this.preview.clientHeight);
+ setTimeout(() => (this.isSyncing = false), 10);
}
});
@@ -451,9 +463,13 @@ export class MarkdownEditor {
this.preview?.addEventListener('scroll', () => {
if (this.syncScroll && !this.isSyncing && this.editor && this.preview) {
this.isSyncing = true;
- const scrollPercentage = this.preview.scrollTop / (this.preview.scrollHeight - this.preview.clientHeight);
- this.editor.scrollTop = scrollPercentage * (this.editor.scrollHeight - this.editor.clientHeight);
- setTimeout(() => this.isSyncing = false, 10);
+ const scrollPercentage =
+ this.preview.scrollTop /
+ (this.preview.scrollHeight - this.preview.clientHeight);
+ this.editor.scrollTop =
+ scrollPercentage *
+ (this.editor.scrollHeight - this.editor.clientHeight);
+ setTimeout(() => (this.isSyncing = false), 10);
}
});
@@ -474,22 +490,30 @@ export class MarkdownEditor {
});
// Settings modal close
- document.getElementById('mdCloseSettings')?.addEventListener('click', () => {
- const modal = document.getElementById('mdSettingsModal');
- if (modal) {
- modal.style.display = 'none';
- }
- });
-
- // Close modal on overlay click
- document.getElementById('mdSettingsModal')?.addEventListener('click', (e) => {
- if ((e.target as HTMLElement).classList.contains('md-editor-modal-overlay')) {
+ document
+ .getElementById('mdCloseSettings')
+ ?.addEventListener('click', () => {
const modal = document.getElementById('mdSettingsModal');
if (modal) {
modal.style.display = 'none';
}
- }
- });
+ });
+
+ // Close modal on overlay click
+ document
+ .getElementById('mdSettingsModal')
+ ?.addEventListener('click', (e) => {
+ if (
+ (e.target as HTMLElement).classList.contains(
+ 'md-editor-modal-overlay'
+ )
+ ) {
+ const modal = document.getElementById('mdSettingsModal');
+ if (modal) {
+ modal.style.display = 'none';
+ }
+ }
+ });
// Settings checkboxes
document.getElementById('mdOptHtml')?.addEventListener('change', (e) => {
@@ -507,10 +531,12 @@ export class MarkdownEditor {
this.updateMarkdownIt();
});
- document.getElementById('mdOptTypographer')?.addEventListener('change', (e) => {
- this.mdOptions.typographer = (e.target as HTMLInputElement).checked;
- this.updateMarkdownIt();
- });
+ document
+ .getElementById('mdOptTypographer')
+ ?.addEventListener('change', (e) => {
+ this.mdOptions.typographer = (e.target as HTMLInputElement).checked;
+ this.updateMarkdownIt();
+ });
// Preset selector
document.getElementById('mdPreset')?.addEventListener('change', (e) => {
@@ -549,7 +575,8 @@ export class MarkdownEditor {
const start = this.editor!.selectionStart;
const end = this.editor!.selectionEnd;
const value = this.editor!.value;
- this.editor!.value = value.substring(0, start) + ' ' + value.substring(end);
+ this.editor!.value =
+ value.substring(0, start) + ' ' + value.substring(end);
this.editor!.selectionStart = this.editor!.selectionEnd = start + 2;
this.updatePreview();
}
@@ -563,18 +590,37 @@ export class MarkdownEditor {
// Update options based on preset
if (preset === 'commonmark') {
- this.mdOptions = { html: false, breaks: false, linkify: false, typographer: false };
+ this.mdOptions = {
+ html: false,
+ breaks: false,
+ linkify: false,
+ typographer: false,
+ };
} else if (preset === 'zero') {
- this.mdOptions = { html: false, breaks: false, linkify: false, typographer: false };
+ this.mdOptions = {
+ html: false,
+ breaks: false,
+ linkify: false,
+ typographer: false,
+ };
} else {
- this.mdOptions = { html: true, breaks: false, linkify: true, typographer: true };
+ this.mdOptions = {
+ html: true,
+ breaks: false,
+ linkify: true,
+ typographer: true,
+ };
}
// Update UI checkboxes
- (document.getElementById('mdOptHtml') as HTMLInputElement).checked = this.mdOptions.html;
- (document.getElementById('mdOptBreaks') as HTMLInputElement).checked = this.mdOptions.breaks;
- (document.getElementById('mdOptLinkify') as HTMLInputElement).checked = this.mdOptions.linkify;
- (document.getElementById('mdOptTypographer') as HTMLInputElement).checked = this.mdOptions.typographer;
+ (document.getElementById('mdOptHtml') as HTMLInputElement).checked =
+ this.mdOptions.html;
+ (document.getElementById('mdOptBreaks') as HTMLInputElement).checked =
+ this.mdOptions.breaks;
+ (document.getElementById('mdOptLinkify') as HTMLInputElement).checked =
+ this.mdOptions.linkify;
+ (document.getElementById('mdOptTypographer') as HTMLInputElement).checked =
+ this.mdOptions.typographer;
this.updateMarkdownIt();
}
@@ -588,7 +634,6 @@ export class MarkdownEditor {
}
}
-
private createMarkdownIt(): MarkdownIt {
// Use preset if commonmark or zero
let md: MarkdownIt;
@@ -604,28 +649,31 @@ export class MarkdownEditor {
highlight: (str: string, lang: string) => {
if (lang && hljs.getLanguage(lang)) {
try {
- return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value;
+ return hljs.highlight(str, {
+ language: lang,
+ ignoreIllegals: true,
+ }).value;
} catch {
// Fall through to default
}
}
return ''; // Use external default escaping
- }
+ },
});
}
// Apply plugins only for default preset (plugins may not work well with commonmark/zero)
if (this.currentPreset === 'default') {
- md.use(sub) // Subscript: ~text~ -> text
- .use(sup) // Superscript: ^text^ -> text
- .use(footnote) // Footnotes: [^1] and [^1]: footnote text
- .use(deflist) // Definition lists
- .use(abbr) // Abbreviations: *[abbr]: full text
- .use(emoji) // Emoji: :smile: -> 😄
- .use(ins) // Inserted text: ++text++ -> text
- .use(mark) // Marked text: ==text== -> text
- .use(taskLists, { enabled: true, label: true, labelAfter: true }) // Task lists: - [x] done
- .use(anchor, { permalink: false }) // Header anchors
+ md.use(sub) // Subscript: ~text~ -> text
+ .use(sup) // Superscript: ^text^ -> text
+ .use(footnote) // Footnotes: [^1] and [^1]: footnote text
+ .use(deflist) // Definition lists
+ .use(abbr) // Abbreviations: *[abbr]: full text
+ .use(emoji) // Emoji: :smile: -> 😄
+ .use(ins) // Inserted text: ++text++ -> text
+ .use(mark) // Marked text: ==text== -> text
+ .use(taskLists, { enabled: true, label: true, labelAfter: true }) // Task lists: - [x] done
+ .use(anchor, { permalink: false }) // Header anchors
.use(tocDoneRight); // Table of contents: ${toc}
}
@@ -650,7 +698,9 @@ export class MarkdownEditor {
private async renderMermaidDiagrams(): Promise {
if (!this.preview) return;
- const mermaidBlocks = this.preview.querySelectorAll('pre > code.language-mermaid');
+ const mermaidBlocks = this.preview.querySelectorAll(
+ 'pre > code.language-mermaid'
+ );
for (let i = 0; i < mermaidBlocks.length; i++) {
const block = mermaidBlocks[i] as HTMLElement;
@@ -948,14 +998,16 @@ ${content}
applyTranslations();
// Special handling for select options (data-i18n on options doesn't work with applyTranslations)
- const presetSelect = document.getElementById('mdPreset') as HTMLSelectElement;
+ const presetSelect = document.getElementById(
+ 'mdPreset'
+ ) as HTMLSelectElement;
if (presetSelect) {
const options = presetSelect.querySelectorAll('option[data-i18n]');
options.forEach((option) => {
const key = option.getAttribute('data-i18n');
if (key) {
// Use i18next directly for option text
- const translated = (window as any).i18next?.t(key);
+ const translated = (window as WindowWithI18next).i18next?.t(key);
if (translated && translated !== key) {
option.textContent = translated;
}
diff --git a/src/js/utils/page-preview.ts b/src/js/utils/page-preview.ts
index a0bac06..678397f 100644
--- a/src/js/utils/page-preview.ts
+++ b/src/js/utils/page-preview.ts
@@ -154,7 +154,7 @@ document.addEventListener('keydown', handleKeydown);
export function initPagePreview(
container: HTMLElement,
pdfjsDoc: PDFDocumentProxy,
- options: { pageAttr?: string } = {}
+ _options: { pageAttr?: string } = {}
): void {
const totalPages = pdfjsDoc.numPages;
diff --git a/src/js/utils/pdf-decrypt.ts b/src/js/utils/pdf-decrypt.ts
index 44caf80..f5237eb 100644
--- a/src/js/utils/pdf-decrypt.ts
+++ b/src/js/utils/pdf-decrypt.ts
@@ -1,5 +1,6 @@
import { getCpdf, isCpdfAvailable } from './cpdf-helper';
import { isPyMuPDFAvailable, loadPyMuPDF } from './pymupdf-loader';
+import type { CpdfInstance } from '@/types';
export type PdfDecryptEngine = 'cpdf' | 'pymupdf';
@@ -37,13 +38,13 @@ function copyBytes(bytes: Uint8Array): Uint8Array {
return Uint8Array.from(bytes);
}
-function cleanupCpdfDocument(cpdf: unknown, pdf: unknown): void {
- if (!cpdf || !pdf || typeof cpdf !== 'object' || !('deletePdf' in cpdf)) {
+function cleanupCpdfDocument(cpdf: CpdfInstance, pdf: unknown): void {
+ if (!cpdf || !pdf) {
return;
}
try {
- (cpdf as { deletePdf: (document: unknown) => void }).deletePdf(pdf);
+ cpdf.deletePdf(pdf);
} catch (cleanupError) {
console.warn(
`${DECRYPT_LOG_PREFIX} Failed to cleanup CoherentPDF document: ${normalizeErrorMessage(cleanupError)}`
diff --git a/src/js/utils/pymupdf-loader.ts b/src/js/utils/pymupdf-loader.ts
index 58b74ba..a424383 100644
--- a/src/js/utils/pymupdf-loader.ts
+++ b/src/js/utils/pymupdf-loader.ts
@@ -1,25 +1,34 @@
import { WasmProvider } from './wasm-provider.js';
+import type {
+ PyMuPDFInstance,
+ PyMuPDFCompressOptions,
+ PyMuPDFExtractTextOptions,
+ PyMuPDFRasterizeOptions,
+} from '@/types';
-let cachedPyMuPDF: any = null;
-let loadPromise: Promise | null = null;
+let cachedPyMuPDF: PyMuPDFInstance | null = null;
+let loadPromise: Promise | null = null;
export interface PyMuPDFInterface {
load(): Promise;
compressPdf(
file: Blob,
- options: any
+ options: PyMuPDFCompressOptions
): Promise<{ blob: Blob; compressedSize: number }>;
convertToPdf(file: Blob, ext: string): Promise;
- extractText(file: Blob, options?: any): Promise;
+ extractText(file: Blob, options?: PyMuPDFExtractTextOptions): Promise;
extractImages(file: Blob): Promise>;
- extractTables(file: Blob): Promise;
+ extractTables(file: Blob): Promise;
toSvg(file: Blob, pageNum: number): Promise;
renderPageToImage(file: Blob, pageNum: number, scale: number): Promise;
getPageCount(file: Blob): Promise;
- rasterizePdf(file: Blob | File, options: any): Promise;
+ rasterizePdf(
+ file: Blob | File,
+ options: PyMuPDFRasterizeOptions
+ ): Promise;
}
-export async function loadPyMuPDF(): Promise {
+export async function loadPyMuPDF(): Promise {
if (cachedPyMuPDF) {
return cachedPyMuPDF;
}
@@ -65,9 +74,10 @@ export async function loadPyMuPDF(): Promise {
console.log('[PyMuPDF Loader] Successfully loaded from CDN');
return cachedPyMuPDF;
- } catch (error: any) {
+ } catch (error: unknown) {
loadPromise = null;
- throw new Error(`Failed to load PyMuPDF from CDN: ${error.message}`, {
+ const msg = error instanceof Error ? error.message : String(error);
+ throw new Error(`Failed to load PyMuPDF from CDN: ${msg}`, {
cause: error,
});
}
diff --git a/src/js/utils/sanitize.ts b/src/js/utils/sanitize.ts
index 6432b86..4fad95b 100644
--- a/src/js/utils/sanitize.ts
+++ b/src/js/utils/sanitize.ts
@@ -1,5 +1,34 @@
-import { PDFDocument, PDFName } from 'pdf-lib';
+import { PDFDocument, PDFName, PDFDict, PDFArray } from 'pdf-lib';
import { loadPdfDocument } from './load-pdf-document.js';
+import type { PDFDocumentInternal } from '@/types';
+
+function getCatalogDict(pdfDoc: PDFDocument): PDFDict {
+ return pdfDoc.catalog;
+}
+
+function lookupAsDict(
+ pdfDoc: PDFDocument,
+ ref: ReturnType
+): PDFDict | undefined {
+ if (!ref) return undefined;
+ const result = pdfDoc.context.lookup(ref);
+ if (result instanceof PDFDict) return result;
+ return undefined;
+}
+
+function lookupAsArray(
+ pdfDoc: PDFDocument,
+ ref: ReturnType
+): PDFArray | undefined {
+ if (!ref) return undefined;
+ const result = pdfDoc.context.lookup(ref);
+ if (result instanceof PDFArray) return result;
+ return undefined;
+}
+
+function getErrorMessage(e: unknown): string {
+ return e instanceof Error ? e.message : String(e);
+}
export interface SanitizeOptions {
flattenForms: boolean;
@@ -28,9 +57,9 @@ export const defaultSanitizeOptions: SanitizeOptions = {
};
function removeMetadataFromDoc(pdfDoc: PDFDocument) {
- const infoDict = (pdfDoc as any).getInfoDict();
+ const infoDict = (pdfDoc as unknown as PDFDocumentInternal).getInfoDict();
const allKeys = infoDict.keys();
- allKeys.forEach((key: any) => {
+ allKeys.forEach((key: PDFName) => {
infoDict.delete(key);
});
@@ -42,30 +71,30 @@ function removeMetadataFromDoc(pdfDoc: PDFDocument) {
pdfDoc.setProducer('');
try {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
if (catalogDict.has(PDFName.of('Metadata'))) {
catalogDict.delete(PDFName.of('Metadata'));
}
- } catch (e: any) {
- console.warn('Could not remove XMP metadata:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not remove XMP metadata:', getErrorMessage(e));
}
try {
const context = pdfDoc.context;
- if ((context as any).trailerInfo) {
- delete (context as any).trailerInfo.ID;
+ if (context.trailerInfo) {
+ delete context.trailerInfo.ID;
}
- } catch (e: any) {
- console.warn('Could not remove document IDs:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not remove document IDs:', getErrorMessage(e));
}
try {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
if (catalogDict.has(PDFName.of('PieceInfo'))) {
catalogDict.delete(PDFName.of('PieceInfo'));
}
- } catch (e: any) {
- console.warn('Could not remove PieceInfo:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not remove PieceInfo:', getErrorMessage(e));
}
}
@@ -74,8 +103,11 @@ function removeAnnotationsFromDoc(pdfDoc: PDFDocument) {
for (const page of pages) {
try {
page.node.delete(PDFName.of('Annots'));
- } catch (e: any) {
- console.warn('Could not remove annotations from page:', e.message);
+ } catch (e: unknown) {
+ console.warn(
+ 'Could not remove annotations from page:',
+ getErrorMessage(e)
+ );
}
}
}
@@ -86,21 +118,22 @@ function flattenFormsInDoc(pdfDoc: PDFDocument) {
}
function removeJavascriptFromDoc(pdfDoc: PDFDocument) {
- if ((pdfDoc as any).javaScripts && (pdfDoc as any).javaScripts.length > 0) {
- (pdfDoc as any).javaScripts = [];
+ const pdfDocInternal = pdfDoc as unknown as PDFDocumentInternal;
+ if (pdfDocInternal.javaScripts && pdfDocInternal.javaScripts.length > 0) {
+ pdfDocInternal.javaScripts = [];
}
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
const namesRef = catalogDict.get(PDFName.of('Names'));
if (namesRef) {
try {
- const namesDict = pdfDoc.context.lookup(namesRef) as any;
- if (namesDict.has(PDFName.of('JavaScript'))) {
+ const namesDict = lookupAsDict(pdfDoc, namesRef);
+ if (namesDict?.has(PDFName.of('JavaScript'))) {
namesDict.delete(PDFName.of('JavaScript'));
}
- } catch (e: any) {
- console.warn('Could not access Names/JavaScript:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not access Names/JavaScript:', getErrorMessage(e));
}
}
@@ -124,50 +157,55 @@ function removeJavascriptFromDoc(pdfDoc: PDFDocument) {
const annotRefs = pageDict.Annots()?.asArray() || [];
for (const annotRef of annotRefs) {
try {
- const annot = pdfDoc.context.lookup(annotRef) as any;
+ const annot = lookupAsDict(pdfDoc, annotRef);
+ if (!annot) continue;
if (annot.has(PDFName.of('A'))) {
const actionRef = annot.get(PDFName.of('A'));
try {
- const actionDict = pdfDoc.context.lookup(actionRef) as any;
+ const actionDict = lookupAsDict(pdfDoc, actionRef);
const actionType = actionDict
- .get(PDFName.of('S'))
+ ?.get(PDFName.of('S'))
?.toString()
.substring(1);
if (actionType === 'JavaScript') {
annot.delete(PDFName.of('A'));
}
- } catch (e: any) {
- console.warn('Could not read action:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not read action:', getErrorMessage(e));
}
}
if (annot.has(PDFName.of('AA'))) {
annot.delete(PDFName.of('AA'));
}
- } catch (e: any) {
- console.warn('Could not process annotation for JS:', e.message);
+ } catch (e: unknown) {
+ console.warn(
+ 'Could not process annotation for JS:',
+ getErrorMessage(e)
+ );
}
}
- } catch (e: any) {
- console.warn('Could not remove page actions:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not remove page actions:', getErrorMessage(e));
}
}
try {
const acroFormRef = catalogDict.get(PDFName.of('AcroForm'));
if (acroFormRef) {
- const acroFormDict = pdfDoc.context.lookup(acroFormRef) as any;
- const fieldsRef = acroFormDict.get(PDFName.of('Fields'));
+ const acroFormDict = lookupAsDict(pdfDoc, acroFormRef);
+ const fieldsRef = acroFormDict?.get(PDFName.of('Fields'));
if (fieldsRef) {
- const fieldsArray = pdfDoc.context.lookup(fieldsRef) as any;
- const fields = fieldsArray.asArray();
+ const fieldsArray = lookupAsArray(pdfDoc, fieldsRef);
+ const fields = fieldsArray?.asArray() || [];
for (const fieldRef of fields) {
try {
- const field = pdfDoc.context.lookup(fieldRef) as any;
+ const field = lookupAsDict(pdfDoc, fieldRef);
+ if (!field) continue;
if (field.has(PDFName.of('A'))) {
field.delete(PDFName.of('A'));
@@ -176,29 +214,29 @@ function removeJavascriptFromDoc(pdfDoc: PDFDocument) {
if (field.has(PDFName.of('AA'))) {
field.delete(PDFName.of('AA'));
}
- } catch (e: any) {
- console.warn('Could not process field for JS:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not process field for JS:', getErrorMessage(e));
}
}
}
}
- } catch (e: any) {
- console.warn('Could not process form fields for JS:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not process form fields for JS:', getErrorMessage(e));
}
}
function removeEmbeddedFilesFromDoc(pdfDoc: PDFDocument) {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
const namesRef = catalogDict.get(PDFName.of('Names'));
if (namesRef) {
try {
- const namesDict = pdfDoc.context.lookup(namesRef) as any;
- if (namesDict.has(PDFName.of('EmbeddedFiles'))) {
+ const namesDict = lookupAsDict(pdfDoc, namesRef);
+ if (namesDict?.has(PDFName.of('EmbeddedFiles'))) {
namesDict.delete(PDFName.of('EmbeddedFiles'));
}
- } catch (e: any) {
- console.warn('Could not access Names/EmbeddedFiles:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not access Names/EmbeddedFiles:', getErrorMessage(e));
}
}
@@ -214,16 +252,16 @@ function removeEmbeddedFilesFromDoc(pdfDoc: PDFDocument) {
for (const ref of annotRefs) {
try {
- const annot = pdfDoc.context.lookup(ref) as any;
+ const annot = lookupAsDict(pdfDoc, ref);
const subtype = annot
- .get(PDFName.of('Subtype'))
+ ?.get(PDFName.of('Subtype'))
?.toString()
.substring(1);
if (subtype !== 'FileAttachment') {
annotsToKeep.push(ref);
}
- } catch (e) {
+ } catch {
annotsToKeep.push(ref);
}
}
@@ -236,18 +274,16 @@ function removeEmbeddedFilesFromDoc(pdfDoc: PDFDocument) {
page.node.delete(PDFName.of('Annots'));
}
}
- } catch (pageError: any) {
+ } catch (pageError: unknown) {
console.warn(
- `Could not process page for attachments: ${pageError.message}`
+ `Could not process page for attachments: ${getErrorMessage(pageError)}`
);
}
}
- if (
- (pdfDoc as any).embeddedFiles &&
- (pdfDoc as any).embeddedFiles.length > 0
- ) {
- (pdfDoc as any).embeddedFiles = [];
+ const pdfDocInternal = pdfDoc as unknown as PDFDocumentInternal;
+ if (pdfDocInternal.embeddedFiles && pdfDocInternal.embeddedFiles.length > 0) {
+ pdfDocInternal.embeddedFiles = [];
}
if (catalogDict.has(PDFName.of('Collection'))) {
@@ -256,7 +292,7 @@ function removeEmbeddedFilesFromDoc(pdfDoc: PDFDocument) {
}
function removeLayersFromDoc(pdfDoc: PDFDocument) {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
if (catalogDict.has(PDFName.of('OCProperties'))) {
catalogDict.delete(PDFName.of('OCProperties'));
@@ -274,16 +310,16 @@ function removeLayersFromDoc(pdfDoc: PDFDocument) {
const resourcesRef = pageDict.get(PDFName.of('Resources'));
if (resourcesRef) {
try {
- const resourcesDict = pdfDoc.context.lookup(resourcesRef) as any;
- if (resourcesDict.has(PDFName.of('Properties'))) {
+ const resourcesDict = lookupAsDict(pdfDoc, resourcesRef);
+ if (resourcesDict?.has(PDFName.of('Properties'))) {
resourcesDict.delete(PDFName.of('Properties'));
}
- } catch (e: any) {
- console.warn('Could not access Resources:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not access Resources:', getErrorMessage(e));
}
}
- } catch (e: any) {
- console.warn('Could not remove page layers:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not remove page layers:', getErrorMessage(e));
}
}
}
@@ -299,8 +335,9 @@ function removeLinksFromDoc(pdfDoc: PDFDocument) {
const annotsRef = pageDict.get(PDFName.of('Annots'));
if (!annotsRef) continue;
- const annotsArray = pdfDoc.context.lookup(annotsRef) as any;
- const annotRefs = annotsArray.asArray();
+ const annotsArrayObj = lookupAsArray(pdfDoc, annotsRef);
+ if (!annotsArrayObj) continue;
+ const annotRefs = annotsArrayObj.asArray();
if (annotRefs.length === 0) continue;
@@ -309,7 +346,11 @@ function removeLinksFromDoc(pdfDoc: PDFDocument) {
for (const ref of annotRefs) {
try {
- const annot = pdfDoc.context.lookup(ref) as any;
+ const annot = lookupAsDict(pdfDoc, ref);
+ if (!annot) {
+ annotsToKeep.push(ref);
+ continue;
+ }
const subtype = annot
.get(PDFName.of('Subtype'))
?.toString()
@@ -324,9 +365,9 @@ function removeLinksFromDoc(pdfDoc: PDFDocument) {
const actionRef = annot.get(PDFName.of('A'));
if (actionRef) {
try {
- const actionDict = pdfDoc.context.lookup(actionRef) as any;
+ const actionDict = lookupAsDict(pdfDoc, actionRef);
const actionType = actionDict
- .get(PDFName.of('S'))
+ ?.get(PDFName.of('S'))
?.toString()
.substring(1);
@@ -339,8 +380,8 @@ function removeLinksFromDoc(pdfDoc: PDFDocument) {
isLink = true;
linksRemoved++;
}
- } catch (e: any) {
- console.warn('Could not read action:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not read action:', getErrorMessage(e));
}
}
@@ -354,8 +395,8 @@ function removeLinksFromDoc(pdfDoc: PDFDocument) {
if (!isLink) {
annotsToKeep.push(ref);
}
- } catch (e: any) {
- console.warn('Could not process annotation:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not process annotation:', getErrorMessage(e));
annotsToKeep.push(ref);
}
}
@@ -368,37 +409,37 @@ function removeLinksFromDoc(pdfDoc: PDFDocument) {
pageDict.delete(PDFName.of('Annots'));
}
}
- } catch (pageError: any) {
+ } catch (pageError: unknown) {
console.warn(
- `Could not process page ${pageIndex + 1} for links: ${pageError.message}`
+ `Could not process page ${pageIndex + 1} for links: ${getErrorMessage(pageError)}`
);
}
}
try {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
const namesRef = catalogDict.get(PDFName.of('Names'));
if (namesRef) {
try {
- const namesDict = pdfDoc.context.lookup(namesRef) as any;
- if (namesDict.has(PDFName.of('Dests'))) {
+ const namesDict = lookupAsDict(pdfDoc, namesRef);
+ if (namesDict?.has(PDFName.of('Dests'))) {
namesDict.delete(PDFName.of('Dests'));
}
- } catch (e: any) {
- console.warn('Could not access Names/Dests:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not access Names/Dests:', getErrorMessage(e));
}
}
if (catalogDict.has(PDFName.of('Dests'))) {
catalogDict.delete(PDFName.of('Dests'));
}
- } catch (e: any) {
- console.warn('Could not remove named destinations:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not remove named destinations:', getErrorMessage(e));
}
}
function removeStructureTreeFromDoc(pdfDoc: PDFDocument) {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
if (catalogDict.has(PDFName.of('StructTreeRoot'))) {
catalogDict.delete(PDFName.of('StructTreeRoot'));
@@ -411,8 +452,8 @@ function removeStructureTreeFromDoc(pdfDoc: PDFDocument) {
if (pageDict.has(PDFName.of('StructParents'))) {
pageDict.delete(PDFName.of('StructParents'));
}
- } catch (e: any) {
- console.warn('Could not remove page StructParents:', e.message);
+ } catch (e: unknown) {
+ console.warn('Could not remove page StructParents:', getErrorMessage(e));
}
}
@@ -422,7 +463,7 @@ function removeStructureTreeFromDoc(pdfDoc: PDFDocument) {
}
function removeMarkInfoFromDoc(pdfDoc: PDFDocument) {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
if (catalogDict.has(PDFName.of('MarkInfo'))) {
catalogDict.delete(PDFName.of('MarkInfo'));
@@ -444,29 +485,29 @@ function removeFontsFromDoc(pdfDoc: PDFDocument) {
if (resourcesRef) {
try {
- const resourcesDict = pdfDoc.context.lookup(resourcesRef) as any;
+ const resourcesDict = lookupAsDict(pdfDoc, resourcesRef);
+ if (!resourcesDict) continue;
if (resourcesDict.has(PDFName.of('Font'))) {
const fontRef = resourcesDict.get(PDFName.of('Font'));
try {
- const fontDict = pdfDoc.context.lookup(fontRef) as any;
+ const fontDict = lookupAsDict(pdfDoc, fontRef);
+ if (!fontDict) continue;
const fontKeys = fontDict.keys();
for (const fontKey of fontKeys) {
try {
const specificFontRef = fontDict.get(fontKey);
- const specificFont = pdfDoc.context.lookup(
- specificFontRef
- ) as any;
+ const specificFont = lookupAsDict(pdfDoc, specificFontRef);
+ if (!specificFont) continue;
if (specificFont.has(PDFName.of('FontDescriptor'))) {
const descriptorRef = specificFont.get(
PDFName.of('FontDescriptor')
);
- const descriptor = pdfDoc.context.lookup(
- descriptorRef
- ) as any;
+ const descriptor = lookupAsDict(pdfDoc, descriptorRef);
+ if (!descriptor) continue;
const fontFileKeys = ['FontFile', 'FontFile2', 'FontFile3'];
for (const key of fontFileKeys) {
@@ -475,28 +516,38 @@ function removeFontsFromDoc(pdfDoc: PDFDocument) {
}
}
}
- } catch (e: any) {
- console.warn(`Could not process font ${fontKey}:`, e.message);
+ } catch (e: unknown) {
+ console.warn(
+ `Could not process font ${fontKey}:`,
+ getErrorMessage(e)
+ );
}
}
- } catch (e: any) {
- console.warn('Could not access font dictionary:', e.message);
+ } catch (e: unknown) {
+ console.warn(
+ 'Could not access font dictionary:',
+ getErrorMessage(e)
+ );
}
}
- } catch (e: any) {
- console.warn('Could not access Resources for fonts:', e.message);
+ } catch (e: unknown) {
+ console.warn(
+ 'Could not access Resources for fonts:',
+ getErrorMessage(e)
+ );
}
}
- } catch (e: any) {
+ } catch (e: unknown) {
console.warn(
`Could not remove fonts from page ${pageIndex + 1}:`,
- e.message
+ getErrorMessage(e)
);
}
}
- if ((pdfDoc as any).fonts && (pdfDoc as any).fonts.length > 0) {
- (pdfDoc as any).fonts = [];
+ const pdfDocInternal = pdfDoc as unknown as PDFDocumentInternal;
+ if (pdfDocInternal.fonts && pdfDocInternal.fonts.length > 0) {
+ pdfDocInternal.fonts = [];
}
}
@@ -509,15 +560,18 @@ export async function sanitizePdf(
if (options.flattenForms) {
try {
flattenFormsInDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not flatten forms: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not flatten forms: ${getErrorMessage(e)}`);
try {
- const catalogDict = (pdfDoc.catalog as any).dict;
+ const catalogDict = getCatalogDict(pdfDoc);
if (catalogDict.has(PDFName.of('AcroForm'))) {
catalogDict.delete(PDFName.of('AcroForm'));
}
- } catch (removeError: any) {
- console.warn('Could not remove AcroForm:', removeError.message);
+ } catch (removeError: unknown) {
+ console.warn(
+ 'Could not remove AcroForm:',
+ getErrorMessage(removeError)
+ );
}
}
}
@@ -533,56 +587,56 @@ export async function sanitizePdf(
if (options.removeJavascript) {
try {
removeJavascriptFromDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not remove JavaScript: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not remove JavaScript: ${getErrorMessage(e)}`);
}
}
if (options.removeEmbeddedFiles) {
try {
removeEmbeddedFilesFromDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not remove embedded files: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not remove embedded files: ${getErrorMessage(e)}`);
}
}
if (options.removeLayers) {
try {
removeLayersFromDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not remove layers: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not remove layers: ${getErrorMessage(e)}`);
}
}
if (options.removeLinks) {
try {
removeLinksFromDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not remove links: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not remove links: ${getErrorMessage(e)}`);
}
}
if (options.removeStructureTree) {
try {
removeStructureTreeFromDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not remove structure tree: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not remove structure tree: ${getErrorMessage(e)}`);
}
}
if (options.removeMarkInfo) {
try {
removeMarkInfoFromDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not remove MarkInfo: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not remove MarkInfo: ${getErrorMessage(e)}`);
}
}
if (options.removeFonts) {
try {
removeFontsFromDoc(pdfDoc);
- } catch (e: any) {
- console.warn(`Could not remove fonts: ${e.message}`);
+ } catch (e: unknown) {
+ console.warn(`Could not remove fonts: ${getErrorMessage(e)}`);
}
}
diff --git a/src/js/utils/wasm-preloader.ts b/src/js/utils/wasm-preloader.ts
index 818e281..451874d 100644
--- a/src/js/utils/wasm-preloader.ts
+++ b/src/js/utils/wasm-preloader.ts
@@ -1,4 +1,3 @@
-import { getLibreOfficeConverter } from './libreoffice-loader.js';
import { isWasmAvailable, getWasmBaseUrl } from '../config/wasm-cdn-config.js';
export enum PreloadStatus {
@@ -77,7 +76,7 @@ async function preloadGhostscript(): Promise {
await import('./ghostscript-loader.js');
const gsModule = await loadGsModule();
- setCachedGsModule(gsModule as any);
+ setCachedGsModule(gsModule);
preloadState.ghostscript = PreloadStatus.READY;
console.log('[Preloader] Ghostscript WASM ready');
} catch (e) {
diff --git a/src/js/workflow/editor.ts b/src/js/workflow/editor.ts
index 0725660..f702275 100644
--- a/src/js/workflow/editor.ts
+++ b/src/js/workflow/editor.ts
@@ -10,7 +10,6 @@ import { DataflowEngine } from 'rete-engine';
import type { DataflowEngineScheme } from 'rete-engine';
import { LitElement, html } from 'lit';
import type { BaseWorkflowNode } from './nodes/base-node';
-// @ts-ignore -- Vite ?inline import for injecting into Shadow DOM
import phosphorCSS from '@phosphor-icons/web/regular?inline';
// Shared stylesheet for Phosphor icons (font-face already loaded globally, strip it)
@@ -305,8 +304,8 @@ export async function createWorkflowEditor(
// Override connection path to use vertical bezier curves (top-to-bottom flow)
litPlugin.addPipe((context) => {
- if ((context as any).type === 'connectionpath') {
- const { points } = (context as any).data;
+ if ('type' in context && context.type === 'connectionpath') {
+ const { points } = context.data;
const [start, end] = points as [
{ x: number; y: number },
{ x: number; y: number },
@@ -318,7 +317,7 @@ export async function createWorkflowEditor(
const path = `M ${start.x} ${start.y} C ${start.x} ${start.y + dy} ${end.x} ${end.y - dy} ${end.x} ${end.y}`;
return {
...context,
- data: { ...(context as any).data, path },
+ data: { ...context.data, path },
} as typeof context;
}
return context;
diff --git a/src/js/workflow/serialization.ts b/src/js/workflow/serialization.ts
index 2b15edb..e184023 100644
--- a/src/js/workflow/serialization.ts
+++ b/src/js/workflow/serialization.ts
@@ -4,26 +4,15 @@ import type { ClassicScheme, LitArea2D } from '@retejs/lit-plugin';
import type { BaseWorkflowNode } from './nodes/base-node';
import { createNodeByType } from './nodes/registry';
import { ClassicPreset } from 'rete';
-import type { SerializedWorkflow } from './types';
+import type {
+ SerializedWorkflow,
+ SerializedNode,
+ SerializedConnection,
+} from './types';
import { WORKFLOW_VERSION } from './types';
type AreaExtra = LitArea2D;
-interface SerializedNode {
- id: string;
- type: string;
- position: { x: number; y: number };
- controls: Record;
-}
-
-interface SerializedConnection {
- id: string;
- source: string;
- sourceOutput: string;
- target: string;
- targetInput: string;
-}
-
function getNodeType(node: BaseWorkflowNode): string | null {
return node.nodeType || null;
}
@@ -78,19 +67,15 @@ async function deserializeWorkflow(
editor: NodeEditor,
area: AreaPlugin
): Promise {
- if (
- !data ||
- !Array.isArray((data as any).nodes) ||
- !Array.isArray((data as any).connections)
- ) {
+ if (!data || !Array.isArray(data.nodes) || !Array.isArray(data.connections)) {
throw new Error(
'Invalid workflow file: missing nodes or connections array.'
);
}
- if ((data as any).version !== WORKFLOW_VERSION) {
+ if (data.version !== WORKFLOW_VERSION) {
console.warn(
- `Workflow version mismatch: expected ${WORKFLOW_VERSION}, got ${(data as any).version}. Attempting load anyway.`
+ `Workflow version mismatch: expected ${WORKFLOW_VERSION}, got ${data.version}. Attempting load anyway.`
);
}
@@ -104,7 +89,7 @@ async function deserializeWorkflow(
const idMap = new Map();
const skippedTypes: string[] = [];
- for (const serializedNode of (data as any).nodes) {
+ for (const serializedNode of data.nodes) {
const node = createNodeByType(serializedNode.type);
if (!node) {
skippedTypes.push(serializedNode.type);
@@ -114,17 +99,17 @@ async function deserializeWorkflow(
for (const [key, value] of Object.entries(serializedNode.controls || {})) {
const control = node.controls[key];
if (control && 'value' in control) {
- (control as any).value = value;
+ (control as { value: unknown }).value = value;
}
}
- await editor.addNode(node as any);
+ await editor.addNode(node as ClassicScheme['Node']);
idMap.set(serializedNode.id, node.id);
await area.translate(node.id, serializedNode.position);
}
- for (const serializedConn of (data as any).connections) {
+ for (const serializedConn of data.connections) {
const sourceId = idMap.get(serializedConn.source);
const targetId = idMap.get(serializedConn.target);
if (!sourceId || !targetId) continue;
@@ -139,7 +124,7 @@ async function deserializeWorkflow(
targetNode,
serializedConn.targetInput
);
- await editor.addConnection(conn as any);
+ await editor.addConnection(conn as ClassicScheme['Connection']);
}
if (skippedTypes.length > 0) {
diff --git a/src/tests/form-creator-extraction.test.ts b/src/tests/form-creator-extraction.test.ts
index d2ff5c8..ae463a5 100644
--- a/src/tests/form-creator-extraction.test.ts
+++ b/src/tests/form-creator-extraction.test.ts
@@ -285,7 +285,7 @@ describe('form creator extraction regression', () => {
});
it('skips fields whose widgets cannot be resolved to any page', async () => {
- const { pdfDoc, page1Field, page2Field } = await buildTwoPageTextFieldPdf();
+ const { pdfDoc, page2Field } = await buildTwoPageTextFieldPdf();
const page2Widget = page2Field.acroField.getWidgets()[0];
const page2WidgetRef = getWidgetRef(page2Widget, pdfDoc);
diff --git a/src/tests/hocr-transform.test.ts b/src/tests/hocr-transform.test.ts
index 844ce0a..0c6e031 100644
--- a/src/tests/hocr-transform.test.ts
+++ b/src/tests/hocr-transform.test.ts
@@ -9,6 +9,7 @@ import {
calculateWordTransform,
calculateSpaceTransform,
} from '../js/utils/hocr-transform';
+import type { OcrWord } from '@/types';
describe('hocr-transform', () => {
describe('parseBBox', () => {
@@ -210,7 +211,7 @@ describe('hocr-transform', () => {
bbox: { x0: 0, y0: 100, x1: 500, y1: 130 },
baseline: { slope: 0, intercept: 0 },
textangle: 0,
- words: [],
+ words: [] as OcrWord[],
direction: 'ltr' as const,
injectWordBreaks: true,
};
@@ -302,7 +303,7 @@ describe('hocr-transform', () => {
bbox: { x0: 0, y0: 100, x1: 500, y1: 130 },
baseline: { slope: 0, intercept: 0 },
textangle: 0,
- words: [],
+ words: [] as OcrWord[],
direction: 'ltr' as const,
injectWordBreaks: true,
};
diff --git a/src/tests/image-effects.test.ts b/src/tests/image-effects.test.ts
index 5850335..4aa7443 100644
--- a/src/tests/image-effects.test.ts
+++ b/src/tests/image-effects.test.ts
@@ -89,7 +89,7 @@ describe('image-effects', () => {
});
it('should handle light colors (l > 0.5)', () => {
- const [h, s, l] = rgbToHsl(255, 128, 128);
+ const [_h, s, l] = rgbToHsl(255, 128, 128);
expect(l).toBeGreaterThan(0.5);
expect(s).toBeGreaterThan(0);
});
diff --git a/src/tests/pdf-decrypt.test.ts b/src/tests/pdf-decrypt.test.ts
index 7d2b6bd..449f712 100644
--- a/src/tests/pdf-decrypt.test.ts
+++ b/src/tests/pdf-decrypt.test.ts
@@ -230,6 +230,7 @@ describe('pdf decrypt', () => {
expect(mockPdfDocumentLoad).toHaveBeenCalledWith(
new Uint8Array([8, 8, 8]),
{
+ ignoreEncryption: true,
throwOnInvalidObject: false,
}
);
@@ -267,6 +268,7 @@ describe('pdf decrypt', () => {
expect(mockPdfDocumentLoad).toHaveBeenCalledWith(
new Uint8Array([6, 6, 6]),
{
+ ignoreEncryption: true,
throwOnInvalidObject: false,
}
);
diff --git a/src/tests/state.test.ts b/src/tests/state.test.ts
index 59ac5d5..f24360d 100644
--- a/src/tests/state.test.ts
+++ b/src/tests/state.test.ts
@@ -21,7 +21,9 @@ describe('State Management', () => {
// 1. Modify the state properties to non-default values
state.activeTool = 'merge';
state.files = [{ name: 'dummy.pdf', size: 1234 } as File];
- state.pdfDoc = { numPages: 5 }; // Mock PDF document object
+ state.pdfDoc = {
+ numPages: 5,
+ } as unknown as import('pdf-lib').PDFDocument;
state.pdfPages = [{}, {}]; // Mock page objects
state.currentPdfUrl = 'blob:http://localhost/some-uuid';
diff --git a/src/tests/timestamp-pdf-page.test.ts b/src/tests/timestamp-pdf-page.test.ts
index dcc0cda..0a3821d 100644
--- a/src/tests/timestamp-pdf-page.test.ts
+++ b/src/tests/timestamp-pdf-page.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach, vi } from 'vitest';
+import { describe, it, expect, beforeEach } from 'vitest';
import { TIMESTAMP_TSA_PRESETS } from '@/js/config/timestamp-tsa';
/**
diff --git a/src/tests/tools.test.ts b/src/tests/tools.test.ts
index f2d9704..b88ef34 100644
--- a/src/tests/tools.test.ts
+++ b/src/tests/tools.test.ts
@@ -23,10 +23,12 @@ describe('Tool Categories Configuration', () => {
// **KEY CHANGE**: This test now ensures IDs are unique only WITHIN this specific category.
it('should not contain any duplicate tool IDs within its own list', () => {
const toolIds = category.tools.map((tool) => {
- if ('id' in tool) return (tool as any).id;
+ if ('id' in tool) return tool.id;
if ('href' in tool) {
- const match = (tool as any).href.match(/\/([^/]+)\.html$/);
- return match ? match[1] : (tool as any).href;
+ const match = (tool as { href: string }).href.match(
+ /\/([^/]+)\.html$/
+ );
+ return match ? match[1] : (tool as { href: string }).href;
}
return 'unknown';
});
diff --git a/src/tests/watermark.test.ts b/src/tests/watermark.test.ts
index 8100760..b524c23 100644
--- a/src/tests/watermark.test.ts
+++ b/src/tests/watermark.test.ts
@@ -61,7 +61,7 @@ describe('Watermark Feature', () => {
});
it('should reset pdfDoc to null', () => {
- state.pdfDoc = {} as any;
+ state.pdfDoc = {} as unknown as typeof state.pdfDoc;
resetState();
@@ -69,7 +69,7 @@ describe('Watermark Feature', () => {
});
it('should reset pdfPages array', () => {
- state.pdfPages = [1, 2, 3] as any;
+ state.pdfPages = [1, 2, 3] as unknown as typeof state.pdfPages;
resetState();
diff --git a/src/types/markdown-it-plugins.d.ts b/src/types/markdown-it-plugins.d.ts
new file mode 100644
index 0000000..6943493
--- /dev/null
+++ b/src/types/markdown-it-plugins.d.ts
@@ -0,0 +1,54 @@
+declare module 'markdown-it-sub' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
+
+declare module 'markdown-it-sup' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
+
+declare module 'markdown-it-footnote' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
+
+declare module 'markdown-it-deflist' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
+
+declare module 'markdown-it-abbr' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
+
+declare module 'markdown-it-emoji' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export { plugin as full };
+ export default plugin;
+}
+
+declare module 'markdown-it-ins' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
+
+declare module 'markdown-it-mark' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
+
+declare module 'markdown-it-task-lists' {
+ import type MarkdownIt from 'markdown-it';
+ const plugin: MarkdownIt.PluginSimple;
+ export default plugin;
+}
diff --git a/tsconfig.json b/tsconfig.json
index c547caf..be85c8a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -42,7 +42,7 @@
"noFallthroughCasesInSwitch": false,
"noUncheckedSideEffectImports": false,
/* Fix for ArrayBuffer type issues */
- "noImplicitAny": false,
+ "noImplicitAny": true,
/* Quality-of-life options */
"isolatedModules": true,
"esModuleInterop": true,