From 8db4ed13701cc6696fc4a6401a6fc07985694f2c Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Sat, 2 May 2026 18:47:08 -0400 Subject: [PATCH] style: add explicit types to eliminate implicit any in index.ts --- flake.nix | 1 + src/driver.ts | 1 + src/index.ts | 65 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/flake.nix b/flake.nix index 43a6fdc..19d1edc 100644 --- a/flake.nix +++ b/flake.nix @@ -65,6 +65,7 @@ nodejs_22 firefox geckodriver + typescript-language-server ]; }; } diff --git a/src/driver.ts b/src/driver.ts index 300e3e5..75c09b6 100644 --- a/src/driver.ts +++ b/src/driver.ts @@ -1,5 +1,6 @@ import { execFileSync } from "node:child_process"; import { Builder, type WebDriver } from "selenium-webdriver"; +export { type WebDriver }; import firefox from "selenium-webdriver/firefox.js"; export interface DriverOptions { diff --git a/src/index.ts b/src/index.ts index 11092f8..2ad7438 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import { loadConfig, type GlimpseConfig } from "./config.js"; -import { createDriver } from "./driver.js"; +import { createDriver, type WebDriver } from "./driver.js"; import { searchKagi } from "./providers/kagi.js"; import { readFileSync, writeFileSync } from "node:fs"; import TurndownService from "turndown"; @@ -11,6 +11,18 @@ const POLL_INTERVAL_MS = 200; const startTime = Date.now(); const runContext: { targetUrl?: string; currentUrl?: string } = {}; +interface ReaderArticle { + title?: string; + byline?: string; + siteName?: string; + html?: string; + text?: string; + readerUrl?: string; + sourceUrl?: string; + finalUrl?: string; + markdown?: string; +} + // Parse CLI Args const [command, ...args] = process.argv.slice(2); const headless = !args.includes("--no-headless"); @@ -23,7 +35,7 @@ const configPath = getOption("--config"); let appConfig: GlimpseConfig = {}; let timeoutMs = DEFAULT_TIMEOUT_MS; -function getOption(name) { +function getOption(name: string) { const prefix = `${name}=`; return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length); } @@ -36,14 +48,14 @@ function elapsedMs() { return Date.now() - startTime; } -function printResult(result) { +function printResult(result: unknown) { if (result === undefined) { return; } const outputValue = result && typeof result === "object" && !Array.isArray(result) - ? { ...result, elapsedMs: result.elapsedMs ?? elapsedMs() } + ? { ...result, elapsedMs: (result as any).elapsedMs ?? elapsedMs() } : result; const output = typeof outputValue === "object" @@ -56,18 +68,18 @@ class CliError extends Error { code: string; details: Record; - constructor(code, message, details = {}) { + constructor(code: string, message: string, details = {}) { super(message); this.code = code; this.details = details; } } -function cliError(code, message, details = {}) { +function cliError(code: string, message: string, details = {}) { throw new CliError(code, message, details); } -function unknownCommand(name) { +function unknownCommand(name: string) { cliError("UNKNOWN_COMMAND", `Unknown command: ${name}`); } @@ -166,8 +178,8 @@ function getPreludeScriptSource() { return inlineJs; } -async function withDriver(action) { - let driver; +async function withDriver(action: (driver: WebDriver) => Promise) { + let driver: WebDriver; try { driver = await createDriver({ headless, existingUrl }); @@ -182,16 +194,16 @@ async function withDriver(action) { } } -async function waitForReadyState(driver) { +async function waitForReadyState(driver: WebDriver) { if (waitUntil === "none") { return; } try { await driver.wait(async () => { - const readyState = await driver.executeScript( + const readyState = (await driver.executeScript( "return document.readyState", - ); + )) as string; return waitUntil === "interactive" ? ["interactive", "complete"].includes(readyState) : readyState === "complete"; @@ -204,14 +216,14 @@ async function waitForReadyState(driver) { } } -async function waitForJs(driver) { +async function waitForJs(driver: WebDriver) { if (!waitJs) { return; } const start = Date.now(); while (Date.now() - start < timeoutMs) { - let result; + let result: unknown; try { result = await driver.executeScript(waitJs); @@ -232,7 +244,7 @@ async function waitForJs(driver) { ); } -async function runPreludeScript(driver) { +async function runPreludeScript(driver: WebDriver) { const scriptSource = getPreludeScriptSource(); if (!scriptSource) { return undefined; @@ -245,10 +257,13 @@ async function runPreludeScript(driver) { } } -async function withPage(targetUrl, action) { +async function withPage( + targetUrl: string, + action: (driver: WebDriver, scriptResult: unknown) => Promise, +) { runContext.targetUrl = targetUrl; - return withDriver(async (driver) => { + return withDriver(async (driver: WebDriver) => { // Navigate To Page try { await driver.get(targetUrl); @@ -340,7 +355,7 @@ async function snapshotCommand() { if (!targetUrl) usage(); - return withPage(targetUrl, async (driver) => { + return withPage(targetUrl, async (driver: WebDriver) => { // Capture Page Metadata const [url, title, result] = await Promise.all([ driver.getCurrentUrl(), @@ -362,7 +377,7 @@ async function execCommand() { if (!targetUrl || (!inlineJs && !scriptPath)) usage(); - return withPage(targetUrl, async (_driver, scriptResult) => scriptResult); + return withPage(targetUrl, async (_driver: WebDriver, scriptResult: unknown) => scriptResult); } async function screenshotCommand() { @@ -371,7 +386,7 @@ async function screenshotCommand() { if (!targetUrl) usage(); - return withPage(targetUrl, async (driver) => { + return withPage(targetUrl, async (driver: WebDriver) => { // Save Screenshot const image = await driver.takeScreenshot(); writeFileSync(outputPath, image, "base64"); @@ -385,11 +400,11 @@ async function screenshotCommand() { }); } -function markdownTitle(text) { +function markdownTitle(text: string) { return text.replaceAll(/\s+/g, " ").trim(); } -function articleToMarkdown(article) { +function articleToMarkdown(article: ReaderArticle) { const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced", @@ -413,7 +428,7 @@ function articleToMarkdown(article) { return `${parts.join("\n\n").trim()}\n`; } -function renderReaderOutput(article, format) { +function renderReaderOutput(article: ReaderArticle, format: string) { switch (format) { case "markdown": return article.markdown; @@ -464,7 +479,7 @@ async function readerCommand() { if (!targetUrl) usage(); - return withPage(targetUrl, async (driver) => { + return withPage(targetUrl, async (driver: WebDriver) => { // Capture Final Url const finalUrl = await driver.getCurrentUrl(); @@ -473,7 +488,7 @@ async function readerCommand() { await driver.get(readerUrl); // Wait For Reader Content - let article; + let article: ReaderArticle; try { article = await driver.wait( async () => {