diff --git a/src/browser.test.ts b/src/browser.test.ts index 189ca7cbd..9a4ce025c 100644 --- a/src/browser.test.ts +++ b/src/browser.test.ts @@ -122,20 +122,32 @@ describe('BrowserBridge state', () => { it('rejects connect() while already connecting', async () => { const bridge = new BrowserBridge(); - (bridge as any)._state = 'connecting'; + (bridge as unknown as { _state: string })._state = 'connecting'; await expect(bridge.connect()).rejects.toThrow('Already connecting'); }); it('rejects connect() while closing', async () => { const bridge = new BrowserBridge(); - (bridge as any)._state = 'closing'; + (bridge as unknown as { _state: string })._state = 'closing'; await expect(bridge.connect()).rejects.toThrow('Session is closing'); }); it('fails fast when daemon is running but extension is disconnected', async () => { - vi.spyOn(daemonClient, 'getDaemonHealth').mockResolvedValue({ state: 'no-extension', status: { extensionConnected: false } as any }); + vi.spyOn(daemonClient, 'getDaemonHealth').mockResolvedValue({ + state: 'no-extension', + status: { + ok: true, + pid: 1, + uptime: 0, + extensionConnected: false, + pending: 0, + lastCliRequestTime: 0, + memoryMB: 0, + port: 0, + }, + }); const bridge = new BrowserBridge(); diff --git a/src/browser/cdp.test.ts b/src/browser/cdp.test.ts index 480f32aee..5da4b71c4 100644 --- a/src/browser/cdp.test.ts +++ b/src/browser/cdp.test.ts @@ -4,13 +4,13 @@ const { MockWebSocket } = vi.hoisted(() => { class MockWebSocket { static OPEN = 1; readyState = 1; - private handlers = new Map void>>(); + private handlers = new Map void>>(); constructor(_url: string) { queueMicrotask(() => this.emit('open')); } - on(event: string, handler: (...args: any[]) => void): void { + on(event: string, handler: (...args: unknown[]) => void): void { const handlers = this.handlers.get(event) ?? []; handlers.push(handler); this.handlers.set(event, handlers); @@ -22,7 +22,7 @@ const { MockWebSocket } = vi.hoisted(() => { this.readyState = 3; } - private emit(event: string, ...args: any[]): void { + private emit(event: string, ...args: unknown[]): void { for (const handler of this.handlers.get(event) ?? []) { handler(...args); } diff --git a/src/browser/dom-helpers.test.ts b/src/browser/dom-helpers.test.ts index e14a2c247..6f2ce4ece 100644 --- a/src/browser/dom-helpers.test.ts +++ b/src/browser/dom-helpers.test.ts @@ -3,7 +3,7 @@ import { autoScrollJs, waitForCaptureJs, waitForSelectorJs } from './dom-helpers describe('autoScrollJs', () => { it('returns early without error when document.body is null', async () => { - const g = globalThis as any; + const g = globalThis as unknown as Record; const origDoc = g.document; g.document = { body: null, documentElement: {} }; g.window = g; @@ -26,19 +26,20 @@ describe('waitForCaptureJs', () => { }); it('resolves "captured" when __opencli_xhr is populated before deadline', async () => { - const g = globalThis as any; - g.__opencli_xhr = []; + const g = globalThis as unknown as Record; + const captured: unknown[] = []; + g.__opencli_xhr = captured; g.window = g; // stub window for Node eval const code = waitForCaptureJs(1000); const promise = eval(code) as Promise; - g.__opencli_xhr.push({ data: 'test' }); + captured.push({ data: 'test' }); await expect(promise).resolves.toBe('captured'); delete g.__opencli_xhr; delete g.window; }); it('rejects when __opencli_xhr stays empty past deadline', async () => { - const g = globalThis as any; + const g = globalThis as unknown as Record; g.__opencli_xhr = []; g.window = g; const code = waitForCaptureJs(50); // 50ms timeout @@ -49,7 +50,7 @@ describe('waitForCaptureJs', () => { }); it('resolves immediately when __opencli_xhr already has data', async () => { - const g = globalThis as any; + const g = globalThis as unknown as Record; g.__opencli_xhr = [{ data: 'already here' }]; g.window = g; const code = waitForCaptureJs(1000); @@ -69,7 +70,7 @@ describe('waitForSelectorJs', () => { }); it('resolves "found" immediately when selector already present', async () => { - const g = globalThis as any; + const g = globalThis as unknown as Record; const fakeEl = { tagName: 'DIV' }; g.document = { querySelector: (_: string) => fakeEl }; const code = waitForSelectorJs('[data-testid="primaryColumn"]', 1000); @@ -78,7 +79,7 @@ describe('waitForSelectorJs', () => { }); it('resolves "found" when selector appears after DOM mutation', async () => { - const g = globalThis as any; + const g = globalThis as unknown as Record; let mutationCallback!: () => void; g.MutationObserver = class { constructor(cb: () => void) { mutationCallback = cb; } @@ -99,7 +100,7 @@ describe('waitForSelectorJs', () => { }); it('rejects when selector never appears within timeout', async () => { - const g = globalThis as any; + const g = globalThis as unknown as Record; g.MutationObserver = class { constructor(_cb: () => void) {} observe() {} diff --git a/src/cascade.ts b/src/cascade.ts index f9aeac98b..e41bb61ff 100644 --- a/src/cascade.ts +++ b/src/cascade.ts @@ -14,6 +14,14 @@ import { Strategy } from './registry.js'; import type { IPage } from './types.js'; import { getErrorMessage } from './errors.js'; +/** Shape returned by the in-page fetch probe JS (see buildFetchProbeJs). */ +interface FetchProbeResponse { + ok?: boolean; + status?: number; + hasData?: boolean; + preview?: string; +} + /** Strategy cascade order (simplest → most complex) */ const CASCADE_ORDER: Strategy[] = [ Strategy.PUBLIC, @@ -103,12 +111,13 @@ export async function probeEndpoint( try { const opts = PROBE_OPTIONS[strategy]; if (opts) { - const resp = await page.evaluate(buildFetchProbeJs(url, opts)); + const resp = (await page.evaluate(buildFetchProbeJs(url, opts))) as FetchProbeResponse | undefined; result.statusCode = resp?.status; - result.success = resp?.ok && resp?.hasData; + result.success = !!(resp?.ok && resp?.hasData); result.hasData = resp?.hasData; result.responsePreview = resp?.preview; } else { + // INTERCEPT / UI require site-specific implementation. result.error = `Strategy ${strategy} requires site-specific implementation`; } } catch (err) { diff --git a/src/cli.ts b/src/cli.ts index 0dbfa7c30..ac5a89510 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -298,7 +298,7 @@ export function createProgram(BUILTIN_CLIS: string, USER_CLIS: string): Command const page = await getBrowserPage(); await fn(page, ...args); } catch (err) { - const msg = err instanceof Error ? err.message : String(err); + const msg = getErrorMessage(err); if (msg.includes('Extension not connected') || msg.includes('Daemon')) { console.error(`Browser not connected. Run 'opencli doctor' to diagnose.`); } else if (msg.includes('attach failed') || msg.includes('chrome-extension://')) { @@ -693,10 +693,12 @@ cli({ console.log(` Executing: opencli ${site} ${command}${limitFlag}\n`); console.log(output); console.log(`\n ✓ Adapter works!`); - } catch (err: any) { + } catch (err) { console.log(` Executing: opencli ${site} ${command}${limitFlag}\n`); - if (err.stdout) console.log(err.stdout); - if (err.stderr) console.error(err.stderr.slice(0, 500)); + // execFileSync attaches captured stdout/stderr on its thrown Error. + const execErr = err as { stdout?: string | Buffer; stderr?: string | Buffer }; + if (execErr.stdout) console.log(String(execErr.stdout)); + if (execErr.stderr) console.error(String(execErr.stderr).slice(0, 500)); console.log(`\n ✗ Adapter failed. Fix the code and try again.`); process.exitCode = EXIT_CODES.GENERIC_ERROR; } diff --git a/src/download/index.ts b/src/download/index.ts index dca99a892..1884b50c8 100644 --- a/src/download/index.ts +++ b/src/download/index.ts @@ -341,7 +341,7 @@ export async function saveDocument( content: string, destPath: string, format: 'json' | 'markdown' | 'html' | 'text' = 'markdown', - metadata?: Record, + metadata?: Record, ): Promise<{ success: boolean; size: number; error?: string }> { try { const dir = path.dirname(destPath); diff --git a/src/engine.test.ts b/src/engine.test.ts index 2e3bdd7b5..41ca4a5fc 100644 --- a/src/engine.test.ts +++ b/src/engine.test.ts @@ -38,13 +38,13 @@ cli({ }); `); - delete (globalThis as any).__opencli_helper_loaded__; + delete (globalThis as { __opencli_helper_loaded__?: unknown }).__opencli_helper_loaded__; await discoverClis(tempRoot); - expect((globalThis as any).__opencli_helper_loaded__).toBeUndefined(); + expect((globalThis as { __opencli_helper_loaded__?: unknown }).__opencli_helper_loaded__).toBeUndefined(); expect(getRegistry().get('temp-site/hello')).toBeDefined(); } finally { - delete (globalThis as any).__opencli_helper_loaded__; + delete (globalThis as { __opencli_helper_loaded__?: unknown }).__opencli_helper_loaded__; await fs.promises.rm(tempRoot, { recursive: true, force: true }); } }); diff --git a/src/output.test.ts b/src/output.test.ts index bf7aec96d..393fdcd70 100644 --- a/src/output.test.ts +++ b/src/output.test.ts @@ -21,7 +21,7 @@ describe('output TTY detection', () => { Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true }); // commanderAdapter always passes fmt:'table' as default — this must still trigger downgrade render([{ name: 'alice', score: 10 }], { fmt: 'table', columns: ['name', 'score'] }); - const out = logSpy.mock.calls.map((c: any[]) => c[0]).join('\n'); + const out = logSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); expect(out).toContain('name: alice'); expect(out).toContain('score: 10'); }); @@ -29,14 +29,14 @@ describe('output TTY detection', () => { it('outputs table in TTY when format is default table', () => { Object.defineProperty(process.stdout, 'isTTY', { value: true, writable: true }); render([{ name: 'alice', score: 10 }], { fmt: 'table', columns: ['name', 'score'] }); - const out = logSpy.mock.calls.map((c: any[]) => c[0]).join('\n'); + const out = logSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); expect(out).toContain('alice'); }); it('respects explicit -f json even in non-TTY', () => { Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true }); render([{ name: 'alice' }], { fmt: 'json' }); - const out = logSpy.mock.calls.map((c: any[]) => c[0]).join('\n'); + const out = logSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); expect(JSON.parse(out)).toEqual([{ name: 'alice' }]); }); @@ -44,7 +44,7 @@ describe('output TTY detection', () => { Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true }); process.env.OUTPUT = 'json'; render([{ name: 'alice' }], { fmt: 'table' }); - const out = logSpy.mock.calls.map((c: any[]) => c[0]).join('\n'); + const out = logSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); expect(JSON.parse(out)).toEqual([{ name: 'alice' }]); }); @@ -52,7 +52,7 @@ describe('output TTY detection', () => { Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true }); process.env.OUTPUT = 'json'; render([{ name: 'alice' }], { fmt: 'csv', fmtExplicit: true }); - const out = logSpy.mock.calls.map((c: any[]) => c[0]).join('\n'); + const out = logSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); expect(out).toContain('name'); expect(out).toContain('alice'); expect(out).not.toContain('"name"'); // not JSON @@ -61,7 +61,7 @@ describe('output TTY detection', () => { it('explicit -f table overrides non-TTY auto-downgrade', () => { Object.defineProperty(process.stdout, 'isTTY', { value: false, writable: true }); render([{ name: 'alice' }], { fmt: 'table', fmtExplicit: true, columns: ['name'] }); - const out = logSpy.mock.calls.map((c: any[]) => c[0]).join('\n'); + const out = logSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); // Should be table output, not YAML expect(out).not.toContain('name: alice'); expect(out).toContain('alice'); diff --git a/src/pipeline/executor.test.ts b/src/pipeline/executor.test.ts index 54bdee210..4b75c2082 100644 --- a/src/pipeline/executor.test.ts +++ b/src/pipeline/executor.test.ts @@ -41,7 +41,7 @@ describe('executePipeline', () => { }); it('skips null/invalid steps', async () => { - const result = await executePipeline(null, [null, undefined, 42] as any); + const result = await executePipeline(null, [null, undefined, 42]); expect(result).toBeNull(); }); diff --git a/src/pipeline/steps/download.ts b/src/pipeline/steps/download.ts index 505d53134..15f92b023 100644 --- a/src/pipeline/steps/download.ts +++ b/src/pipeline/steps/download.ts @@ -101,32 +101,55 @@ function dedupeCookies( * type: auto * ``` */ +interface DownloadParams { + url?: string; + dir?: string; + filename?: string; + concurrency?: number; + skip_existing?: boolean; + timeout?: number; + use_ytdlp?: boolean; + ytdlp_args?: unknown; + type?: string; + progress?: boolean; + content?: string; + metadata?: Record; +} + export async function stepDownload( page: IPage | null, - params: any, - data: any, - args: Record, -): Promise { + params: unknown, + data: unknown, + args: Record, +): Promise { // Parse parameters with defaults - const urlTemplate = typeof params === 'string' ? params : (params?.url ?? ''); - const dirTemplate = params?.dir ?? './downloads'; - const filenameTemplate = params?.filename ?? ''; - const concurrency = typeof params?.concurrency === 'number' ? params.concurrency : 3; - const skipExisting = params?.skip_existing !== false; - const timeout = typeof params?.timeout === 'number' ? params.timeout * 1000 : 30000; - const useYtdlp = params?.use_ytdlp ?? false; - const ytdlpArgs = Array.isArray(params?.ytdlp_args) ? params.ytdlp_args : []; - const contentType = params?.type ?? 'auto'; - const showProgress = params?.progress !== false; - const contentTemplate = params?.content; - const metadataTemplate = params?.metadata; + const p: DownloadParams = + typeof params === 'object' && params !== null ? (params as DownloadParams) : {}; + const urlTemplate = typeof params === 'string' ? params : (p.url ?? ''); + const dirTemplate = p.dir ?? './downloads'; + const filenameTemplate = p.filename ?? ''; + const concurrency = typeof p.concurrency === 'number' ? p.concurrency : 3; + const skipExisting = p.skip_existing !== false; + const timeout = typeof p.timeout === 'number' ? p.timeout * 1000 : 30000; + const useYtdlp = p.use_ytdlp ?? false; + const ytdlpArgs: string[] = Array.isArray(p.ytdlp_args) + ? p.ytdlp_args.map((v) => String(v)) + : []; + const contentType = p.type ?? 'auto'; + const showProgress = p.progress !== false; + const contentTemplate = p.content; + const metadataTemplate = p.metadata; // Resolve output directory const dir = String(render(dirTemplate, { args, data })); fs.mkdirSync(dir, { recursive: true }); - // Normalize data to array - const items: any[] = Array.isArray(data) ? data : data ? [data] : []; + // Normalize data to array. Items are row records (string-keyed) produced by + // upstream steps; we treat them as Record and narrow per-use. + const items: Array> = + Array.isArray(data) ? (data as Array>) + : data ? [data as Record] + : []; if (items.length === 0) { return []; } @@ -171,7 +194,8 @@ export async function stepDownload( } // Process downloads with concurrency - const results = await mapConcurrent(items, concurrency, async (item, index): Promise => { + type DownloadedItem = Record & { _download: DownloadResult }; + const results = await mapConcurrent(items, concurrency, async (item, index): Promise => { const startTime = Date.now(); // Render URL diff --git a/src/pipeline/steps/intercept.ts b/src/pipeline/steps/intercept.ts index 442d6c28d..c3c15faac 100644 --- a/src/pipeline/steps/intercept.ts +++ b/src/pipeline/steps/intercept.ts @@ -5,8 +5,20 @@ import type { IPage } from '../../types.js'; import { render, normalizeEvaluateSource } from '../template.js'; -export async function stepIntercept(page: IPage | null, params: any, data: any, args: Record): Promise { - const cfg = typeof params === 'object' ? params : {}; +interface InterceptParams { + trigger?: string; + capture?: string; + timeout?: number; + select?: string; +} + +export async function stepIntercept( + page: IPage | null, + params: unknown, + data: unknown, + args: Record, +): Promise { + const cfg: InterceptParams = typeof params === 'object' && params !== null ? (params as InterceptParams) : {}; const trigger = cfg.trigger ?? ''; const capturePattern = cfg.capture ?? ''; const timeout = cfg.timeout ?? 8; @@ -38,14 +50,14 @@ export async function stepIntercept(page: IPage | null, params: any, data: any, const matchingResponses = await page!.getInterceptedRequests(); // Step 5: Select from response if specified - let result = matchingResponses.length === 1 ? matchingResponses[0] : + let result: unknown = matchingResponses.length === 1 ? matchingResponses[0] : matchingResponses.length > 1 ? matchingResponses : data; if (selectPath && result) { - let current = result; + let current: unknown = result; for (const part of String(selectPath).split('.')) { if (current && typeof current === 'object' && !Array.isArray(current)) { - current = current[part]; + current = (current as Record)[part]; } else break; } result = current ?? result; diff --git a/src/pipeline/steps/tap.ts b/src/pipeline/steps/tap.ts index 9a58a20f0..372d30805 100644 --- a/src/pipeline/steps/tap.ts +++ b/src/pipeline/steps/tap.ts @@ -13,15 +13,30 @@ import type { IPage } from '../../types.js'; import { render } from '../template.js'; import { generateTapInterceptorJs } from '../../interceptor.js'; -export async function stepTap(page: IPage | null, params: any, data: any, args: Record): Promise { - const cfg = typeof params === 'object' ? params : {}; +interface TapParams { + store?: string; + action?: string; + capture?: string; + timeout?: number; + select?: string; + framework?: string | null; + args?: unknown[]; +} + +export async function stepTap( + page: IPage | null, + params: unknown, + data: unknown, + args: Record, +): Promise { + const cfg: TapParams = typeof params === 'object' && params !== null ? (params as TapParams) : {}; const storeName = String(render(cfg.store ?? '', { args, data })); const actionName = String(render(cfg.action ?? '', { args, data })); const capturePattern = String(render(cfg.capture ?? '', { args, data })); const timeout = cfg.timeout ?? 5; const selectPath = cfg.select ? String(render(cfg.select, { args, data })) : null; const framework = cfg.framework ?? null; - const actionArgs = cfg.args ?? []; + const actionArgs: unknown[] = cfg.args ?? []; if (!storeName || !actionName) throw new Error('tap: store and action are required'); @@ -31,7 +46,7 @@ export async function stepTap(page: IPage | null, params: any, data: any, args: : ''; // Serialize action arguments - const actionArgsRendered = actionArgs.map((a: any) => { + const actionArgsRendered = actionArgs.map((a) => { const rendered = render(a, { args, data }); return JSON.stringify(rendered); }); diff --git a/src/registry.ts b/src/registry.ts index f9a9d4f2a..df49c64b0 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -28,7 +28,6 @@ export interface RequiredEnv { help?: string; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- kwargs from CLI parsing are inherently untyped export type CommandArgs = Record; export interface CliCommand { diff --git a/src/runtime-detect.test.ts b/src/runtime-detect.test.ts index a207b7ad2..f82db399c 100644 --- a/src/runtime-detect.test.ts +++ b/src/runtime-detect.test.ts @@ -19,7 +19,7 @@ describe('runtime-detect', () => { }); it('detects the current environment correctly', () => { - const isBun = typeof (globalThis as any).Bun !== 'undefined'; + const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined'; const rt = detectRuntime(); if (isBun) { expect(rt).toBe('bun'); diff --git a/src/runtime-detect.ts b/src/runtime-detect.ts index fef482e68..45bb0883d 100644 --- a/src/runtime-detect.ts +++ b/src/runtime-detect.ts @@ -8,13 +8,17 @@ export type Runtime = 'bun' | 'node'; +/** Shape of `globalThis` when running under Bun. */ +interface BunGlobal { + Bun?: { version: string }; +} + /** * Detect the current JavaScript runtime. */ export function detectRuntime(): Runtime { // Bun always exposes globalThis.Bun (including Bun.version) - if (typeof (globalThis as any).Bun !== 'undefined') return 'bun'; - return 'node'; + return (globalThis as BunGlobal).Bun !== undefined ? 'bun' : 'node'; } /** @@ -22,10 +26,8 @@ export function detectRuntime(): Runtime { * Examples: "v22.13.0" (Node), "1.1.42" (Bun) */ export function getRuntimeVersion(): string { - if (detectRuntime() === 'bun') { - return (globalThis as any).Bun.version as string; - } - return process.version; // e.g. "v22.13.0" + const bun = (globalThis as BunGlobal).Bun; + return bun ? bun.version : process.version; } /** diff --git a/src/scripts/framework.ts b/src/scripts/framework.ts index ad70c6e2c..1f5eaed5e 100644 --- a/src/scripts/framework.ts +++ b/src/scripts/framework.ts @@ -1,16 +1,35 @@ /** * Injected script for detecting frontend frameworks (Vue, React, Next, Nuxt, etc.) + * + * Serialized via `.toString()` and evaluated in the page context. Types here are + * only for the TS boundary — see scripts/store.ts for the same pattern. */ + +interface VueAppEl { + __vue__?: unknown; + __vue_app__?: { + config?: { + globalProperties?: { $pinia?: unknown; $store?: unknown }; + }; + }; +} +interface FrameworkWindow extends Window { + __REACT_DEVTOOLS_GLOBAL_HOOK__?: unknown; + __NEXT_DATA__?: unknown; + __NUXT__?: unknown; +} + export function detectFramework() { const r: Record = {}; try { - const app = document.querySelector('#app') as any; + const app = document.querySelector('#app') as unknown as VueAppEl | null; + const w = window as FrameworkWindow; r.vue3 = !!(app && app.__vue_app__); r.vue2 = !!(app && app.__vue__); - r.react = !!(window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__ || !!document.querySelector('[data-reactroot]'); - r.nextjs = !!(window as any).__NEXT_DATA__; - r.nuxt = !!(window as any).__NUXT__; - if (r.vue3 && app.__vue_app__) { + r.react = !!w.__REACT_DEVTOOLS_GLOBAL_HOOK__ || !!document.querySelector('[data-reactroot]'); + r.nextjs = !!w.__NEXT_DATA__; + r.nuxt = !!w.__NUXT__; + if (r.vue3 && app?.__vue_app__) { const gp = app.__vue_app__.config?.globalProperties; r.pinia = !!(gp && gp.$pinia); r.vuex = !!(gp && gp.$store); diff --git a/src/scripts/store.ts b/src/scripts/store.ts index 996aad7f1..831ca88bc 100644 --- a/src/scripts/store.ts +++ b/src/scripts/store.ts @@ -1,17 +1,39 @@ /** - * Injected script for discovering Pinia or Vuex stores and their actions/state representations + * Injected script for discovering Pinia or Vuex stores and their actions/state representations. + * + * This function is serialized via `.toString()` and evaluated inside the page context, + * so the types below only exist at the TS boundary — the runtime shapes are whatever + * Pinia/Vuex put on the Vue app. We use narrow structural types for the fields we touch. */ + +// Minimal structural types describing just the fields we access. +type PiniaStore = Record; +interface VuexModule { + _rawModule?: { actions?: Record }; + state?: Record; +} +interface VueApp { + __vue_app__?: { + config?: { + globalProperties?: { + $pinia?: { _s?: Map }; + $store?: { _modules?: { root?: { _children?: Record } } }; + }; + }; + }; +} + export function discoverStores() { const stores: Array<{ type: string; id: string; actions: string[]; stateKeys: string[] }> = []; try { - const app = document.querySelector('#app') as any; + const app = document.querySelector('#app') as unknown as VueApp | null; if (!app?.__vue_app__) return stores; const gp = app.__vue_app__.config?.globalProperties; // Pinia stores const pinia = gp?.$pinia; if (pinia?._s) { - pinia._s.forEach((store: any, id: string) => { + pinia._s.forEach((store, id) => { const actions: string[] = []; const stateKeys: string[] = []; for (const k in store) { @@ -29,7 +51,7 @@ export function discoverStores() { const vuex = gp?.$store; if (vuex?._modules?.root?._children) { const children = vuex._modules.root._children; - for (const [modName, mod] of Object.entries(children)) { + for (const [modName, mod] of Object.entries(children)) { const actions = Object.keys(mod._rawModule?.actions ?? {}).slice(0, 20); const stateKeys = Object.keys(mod.state ?? {}).slice(0, 15); stores.push({ type: 'vuex', id: modName, actions, stateKeys }); diff --git a/src/snapshotFormatter.test.ts b/src/snapshotFormatter.test.ts index 2a517d15f..22ed1a6f2 100644 --- a/src/snapshotFormatter.test.ts +++ b/src/snapshotFormatter.test.ts @@ -174,8 +174,8 @@ describe('formatSnapshot', () => { describe('basic behavior', () => { it('returns empty string for empty/null input', () => { expect(formatSnapshot('')).toBe(''); - expect(formatSnapshot(null as any)).toBe(''); - expect(formatSnapshot(undefined as any)).toBe(''); + expect(formatSnapshot(null as unknown as string)).toBe(''); + expect(formatSnapshot(undefined as unknown as string)).toBe(''); }); it('strips [ref=...] and [cursor=...] annotations', () => {