Skip to content

Commit 45cee57

Browse files
authored
refactor: reduce core any usage (#216)
1 parent 4e32599 commit 45cee57

File tree

5 files changed

+241
-50
lines changed

5 files changed

+241
-50
lines changed

src/cli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
140140
: undefined;
141141
const workspace = `explore:${inferHost(url, opts.site)}`;
142142
const result = await exploreUrl(url, {
143-
BrowserFactory: getBrowserFactory() as any,
143+
BrowserFactory: getBrowserFactory(),
144144
site: opts.site,
145145
goal: opts.goal,
146146
waitSeconds: parseFloat(opts.wait),
@@ -172,7 +172,7 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
172172
const workspace = `generate:${inferHost(url, opts.site)}`;
173173
const r = await generateCliFromUrl({
174174
url,
175-
BrowserFactory: getBrowserFactory() as any,
175+
BrowserFactory: getBrowserFactory(),
176176
builtinClis: BUILTIN_CLIS,
177177
userClis: USER_CLIS,
178178
goal: opts.goal,

src/execution.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ import { getBrowserFactory, browserSession, runWithTimeout, DEFAULT_BROWSER_COMM
1818

1919
/** Set of TS module paths that have been loaded */
2020
const _loadedModules = new Set<string>();
21+
type CommandArgs = Record<string, unknown>;
22+
23+
function getErrorMessage(error: unknown): string {
24+
return error instanceof Error ? error.message : String(error);
25+
}
2126

2227
/**
2328
* Validates and coerces arguments based on the command's Arg definitions.
2429
*/
25-
export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: Record<string, any>): Record<string, any> {
26-
const result: Record<string, any> = { ...kwargs };
30+
export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: CommandArgs): CommandArgs {
31+
const result: CommandArgs = { ...kwargs };
2732

2833
for (const argDef of cmdArgs) {
2934
const val = result[argDef.name];
@@ -72,9 +77,9 @@ export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: Record<string, any
7277
async function runCommand(
7378
cmd: CliCommand,
7479
page: IPage | null,
75-
kwargs: Record<string, any>,
80+
kwargs: CommandArgs,
7681
debug: boolean,
77-
): Promise<any> {
82+
): Promise<unknown> {
7883
// Lazy-load TS module on first execution (manifest fast-path)
7984
const internal = cmd as InternalCliCommand;
8085
if (internal._lazy && internal._modulePath) {
@@ -83,9 +88,9 @@ async function runCommand(
8388
try {
8489
await import(`file://${modulePath}`);
8590
_loadedModules.add(modulePath);
86-
} catch (err: any) {
91+
} catch (err) {
8792
throw new AdapterLoadError(
88-
`Failed to load adapter module ${modulePath}: ${err.message}`,
93+
`Failed to load adapter module ${modulePath}: ${getErrorMessage(err)}`,
8994
'Check that the adapter file exists and has no syntax errors.',
9095
);
9196
}
@@ -127,14 +132,14 @@ function resolvePreNav(cmd: CliCommand): string | null {
127132
*/
128133
export async function executeCommand(
129134
cmd: CliCommand,
130-
rawKwargs: Record<string, any>,
135+
rawKwargs: CommandArgs,
131136
debug: boolean = false,
132-
): Promise<any> {
133-
let kwargs: Record<string, any>;
137+
): Promise<unknown> {
138+
let kwargs: CommandArgs;
134139
try {
135140
kwargs = coerceAndValidateArgs(cmd.args, rawKwargs);
136-
} catch (err: any) {
137-
throw new Error(`[Argument Validation Error]\n${err.message}`);
141+
} catch (err) {
142+
throw new Error(`[Argument Validation Error]\n${getErrorMessage(err)}`);
138143
}
139144

140145
if (shouldUseBrowserSession(cmd)) {

src/explore.ts

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import * as fs from 'node:fs';
1010
import * as path from 'node:path';
1111
import { DEFAULT_BROWSER_EXPLORE_TIMEOUT, browserSession, runWithTimeout } from './runtime.js';
12+
import type { IBrowserFactory } from './runtime.js';
1213
import { VOLATILE_PARAMS, SEARCH_PARAMS, PAGINATION_PARAMS, LIMIT_PARAMS, FIELD_ROLES } from './constants.js';
1314
import { detectFramework } from './scripts/framework.js';
1415
import { discoverStores } from './scripts/store.js';
1516
import { interactFuzz } from './scripts/interact.js';
17+
import type { IPage } from './types.js';
1618

1719
// ── Site name detection ────────────────────────────────────────────────────
1820

@@ -68,7 +70,61 @@ interface InferredCapability {
6870
name: string; description: string; strategy: string; confidence: number;
6971
endpoint: string; itemPath: string | null;
7072
recommendedColumns: string[];
71-
recommendedArgs: Array<{ name: string; type: string; required: boolean; default?: any }>;
73+
recommendedArgs: Array<{ name: string; type: string; required: boolean; default?: unknown }>;
74+
storeHint?: { store: string; action: string };
75+
}
76+
77+
export interface ExploreManifest {
78+
site: string;
79+
target_url: string;
80+
final_url: string;
81+
title: string;
82+
framework: Record<string, boolean>;
83+
stores: Array<{ type: DiscoveredStore['type']; id: string; actions: string[] }>;
84+
top_strategy: string;
85+
explored_at?: string;
86+
}
87+
88+
export interface ExploreAuthSummary {
89+
top_strategy: string;
90+
indicators: string[];
91+
framework: Record<string, boolean>;
92+
}
93+
94+
export interface ExploreEndpointArtifact {
95+
pattern: string;
96+
method: string;
97+
url: string;
98+
status: number | null;
99+
contentType: string;
100+
score: number;
101+
queryParams: string[];
102+
itemPath: string | null;
103+
itemCount: number;
104+
detectedFields: Record<string, string>;
105+
authIndicators: string[];
106+
}
107+
108+
export interface ExploreResult {
109+
site: string;
110+
target_url: string;
111+
final_url: string;
112+
title: string;
113+
framework: Record<string, boolean>;
114+
stores: DiscoveredStore[];
115+
top_strategy: string;
116+
endpoint_count: number;
117+
api_endpoint_count: number;
118+
capabilities: InferredCapability[];
119+
auth_indicators: string[];
120+
out_dir: string;
121+
}
122+
123+
export interface ExploreBundle {
124+
manifest: ExploreManifest;
125+
endpoints: ExploreEndpointArtifact[];
126+
capabilities: InferredCapability[];
127+
auth: ExploreAuthSummary;
72128
}
73129

74130
/**
@@ -167,7 +223,7 @@ function flattenFields(obj: unknown, prefix: string, maxDepth: number): string[]
167223
return names;
168224
}
169225

170-
function scoreEndpoint(ep: { contentType: string; responseAnalysis: any; pattern: string; status: number | null; hasSearchParam: boolean; hasPaginationParam: boolean; hasLimitParam: boolean }): number {
226+
function scoreEndpoint(ep: { contentType: string; responseAnalysis: AnalyzedEndpoint['responseAnalysis']; pattern: string; status: number | null; hasSearchParam: boolean; hasPaginationParam: boolean; hasLimitParam: boolean }): number {
171227
let s = 0;
172228
if (ep.contentType.includes('json')) s += 10;
173229
if (ep.responseAnalysis) { s += 5; s += Math.min(ep.responseAnalysis.itemCount, 10); s += Object.keys(ep.responseAnalysis.detectedFields).length * 2; }
@@ -321,7 +377,7 @@ function inferCapabilitiesFromEndpoints(
321377
/** Write explore artifacts (manifest, endpoints, capabilities, auth, stores) to disk. */
322378
async function writeExploreArtifacts(
323379
targetDir: string,
324-
result: Record<string, any>,
380+
result: Omit<ExploreResult, 'out_dir'>,
325381
analyzedEndpoints: AnalyzedEndpoint[],
326382
stores: DiscoveredStore[],
327383
): Promise<void> {
@@ -354,12 +410,12 @@ async function writeExploreArtifacts(
354410
export async function exploreUrl(
355411
url: string,
356412
opts: {
357-
BrowserFactory: new () => any;
413+
BrowserFactory: new () => IBrowserFactory;
358414
site?: string; goal?: string; authenticated?: boolean;
359415
outDir?: string; waitSeconds?: number; query?: string;
360416
clickLabels?: string[]; auto?: boolean; workspace?: string;
361417
},
362-
): Promise<Record<string, any>> {
418+
): Promise<ExploreResult> {
363419
const waitSeconds = opts.waitSeconds ?? 3.0;
364420
const exploreTimeout = Math.max(DEFAULT_BROWSER_EXPLORE_TIMEOUT, 45.0 + waitSeconds * 8.0);
365421

@@ -467,7 +523,7 @@ export async function exploreUrl(
467523
}, { workspace: opts.workspace });
468524
}
469525

470-
export function renderExploreSummary(result: Record<string, any>): string {
526+
export function renderExploreSummary(result: ExploreResult): string {
471527
const lines = [
472528
'opencli probe: OK', `Site: ${result.site}`, `URL: ${result.target_url}`,
473529
`Title: ${result.title || '(none)'}`, `Strategy: ${result.top_strategy}`,
@@ -492,7 +548,7 @@ export function renderExploreSummary(result: Record<string, any>): string {
492548
return lines.join('\n');
493549
}
494550

495-
async function readPageMetadata(page: any /* IPage */): Promise<{ url: string; title: string }> {
551+
async function readPageMetadata(page: IPage): Promise<{ url: string; title: string }> {
496552
try {
497553
const result = await page.evaluate(`() => ({ url: window.location.href, title: document.title || '' })`);
498554
if (result && typeof result === 'object') return { url: String(result.url ?? ''), title: String(result.title ?? '') };

src/generate.ts

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,59 @@
99
*/
1010

1111
import { exploreUrl } from './explore.js';
12-
import { synthesizeFromExplore } from './synthesize.js';
12+
import type { IBrowserFactory } from './runtime.js';
13+
import { synthesizeFromExplore, type SynthesizeCandidateSummary, type SynthesizeResult } from './synthesize.js';
1314

1415
// TODO: implement real CLI registration (copy candidate YAML to user clis dir)
15-
function registerCandidates(_opts: any): any { return { ok: true, count: 0 }; }
16+
interface RegisterCandidatesOptions {
17+
target: string;
18+
builtinClis?: string;
19+
userClis?: string;
20+
name?: string;
21+
}
22+
23+
interface RegisterCandidatesResult {
24+
ok: boolean;
25+
count: number;
26+
}
27+
28+
export interface GenerateCliOptions {
29+
url: string;
30+
BrowserFactory: new () => IBrowserFactory;
31+
builtinClis?: string;
32+
userClis?: string;
33+
goal?: string | null;
34+
site?: string;
35+
waitSeconds?: number;
36+
top?: number;
37+
register?: boolean;
38+
workspace?: string;
39+
}
40+
41+
export interface GenerateCliResult {
42+
ok: boolean;
43+
goal?: string | null;
44+
normalized_goal?: string | null;
45+
site: string;
46+
selected_candidate: SynthesizeCandidateSummary | null;
47+
selected_command: string;
48+
explore: {
49+
endpoint_count: number;
50+
api_endpoint_count: number;
51+
capability_count: number;
52+
top_strategy: string;
53+
framework: Record<string, boolean>;
54+
};
55+
synthesize: {
56+
candidate_count: number;
57+
candidates: Array<Pick<SynthesizeCandidateSummary, 'name' | 'strategy' | 'confidence'>>;
58+
};
59+
register: RegisterCandidatesResult | null;
60+
}
61+
62+
function registerCandidates(_opts: RegisterCandidatesOptions): RegisterCandidatesResult {
63+
return { ok: true, count: 0 };
64+
}
1665

1766
const CAPABILITY_ALIASES: Record<string, string[]> = {
1867
search: ['search', '搜索', '查找', 'query', 'keyword'],
@@ -41,7 +90,7 @@ function normalizeGoal(goal?: string | null): string | null {
4190
/**
4291
* Select the best candidate matching the user's goal.
4392
*/
44-
function selectCandidate(candidates: any[], goal?: string | null): any {
93+
function selectCandidate(candidates: SynthesizeResult['candidates'], goal?: string | null): SynthesizeCandidateSummary | null {
4594
if (!candidates.length) return null;
4695
if (!goal) return candidates[0]; // highest confidence first
4796

@@ -58,12 +107,12 @@ function selectCandidate(candidates: any[], goal?: string | null): any {
58107
return partial ?? candidates[0];
59108
}
60109

61-
export async function generateCliFromUrl(opts: any): Promise<any> {
110+
export async function generateCliFromUrl(opts: GenerateCliOptions): Promise<GenerateCliResult> {
62111
// Step 1: Deep Explore
63112
const exploreResult = await exploreUrl(opts.url, {
64113
BrowserFactory: opts.BrowserFactory,
65114
site: opts.site,
66-
goal: normalizeGoal(opts.goal) ?? opts.goal,
115+
goal: normalizeGoal(opts.goal) ?? opts.goal ?? undefined,
67116
waitSeconds: opts.waitSeconds ?? 3,
68117
workspace: opts.workspace,
69118
});
@@ -75,10 +124,10 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
75124

76125
// Step 3: Select best candidate for goal
77126
const selected = selectCandidate(synthesizeResult.candidates ?? [], opts.goal);
78-
const selectedSite = selected?.site ?? synthesizeResult.site ?? exploreResult.site;
127+
const selectedSite = synthesizeResult.site ?? exploreResult.site;
79128

80129
// Step 4: Register (if requested)
81-
let registerResult: any = null;
130+
let registerResult: RegisterCandidatesResult | null = null;
82131
if (opts.register !== false && synthesizeResult.candidate_count > 0) {
83132
try {
84133
registerResult = registerCandidates({
@@ -108,7 +157,7 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
108157
},
109158
synthesize: {
110159
candidate_count: synthesizeResult.candidate_count,
111-
candidates: (synthesizeResult.candidates ?? []).map((c: any) => ({
160+
candidates: (synthesizeResult.candidates ?? []).map((c) => ({
112161
name: c.name,
113162
strategy: c.strategy,
114163
confidence: c.confidence,
@@ -118,7 +167,7 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
118167
};
119168
}
120169

121-
export function renderGenerateSummary(r: any): string {
170+
export function renderGenerateSummary(r: GenerateCliResult): string {
122171
const lines = [
123172
`opencli generate: ${r.ok ? 'OK' : 'FAIL'}`,
124173
`Site: ${r.site}`,

0 commit comments

Comments
 (0)