mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
fix(types): add runtime validation for GitHub API response in skill-hub-routes
- Replace GitHubTreeEntry with GitHubContentEntry matching actual API response
- Add runtime array validation to prevent unexpected response formats
- Normalize GitHub API types ('file'/'dir') to internal types ('blob'/'tree')
- Validate required fields (name, path, type) for each entry
This fixes potential runtime errors when GitHub API response structure
differs from expected type definition.
This commit is contained in:
@@ -462,20 +462,37 @@ function buildDownloadUrlFromPath(skillPath: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch skill directory contents from GitHub API
|
||||
* Returns list of files in the directory
|
||||
* GitHub Contents API response entry
|
||||
* @see https://docs.github.com/en/rest/repos/contents#get-repository-content
|
||||
*/
|
||||
interface GitHubTreeEntry {
|
||||
interface GitHubContentEntry {
|
||||
name: string;
|
||||
path: string;
|
||||
sha: string;
|
||||
size?: number;
|
||||
type: 'file' | 'dir' | 'submodule' | 'symlink';
|
||||
download_url?: string;
|
||||
url: string;
|
||||
html_url?: string;
|
||||
git_url?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal normalized entry type for processing
|
||||
*/
|
||||
interface NormalizedTreeEntry {
|
||||
path: string;
|
||||
mode: string;
|
||||
type: 'blob' | 'tree';
|
||||
sha: string;
|
||||
size?: number;
|
||||
url: string;
|
||||
}
|
||||
|
||||
async function fetchSkillDirectoryContents(skillPath: string): Promise<GitHubTreeEntry[]> {
|
||||
// Use GitHub API to get tree contents
|
||||
/**
|
||||
* Fetch skill directory contents from GitHub Contents API
|
||||
* Returns normalized list of files and directories
|
||||
*/
|
||||
async function fetchSkillDirectoryContents(skillPath: string): Promise<NormalizedTreeEntry[]> {
|
||||
const apiUrl = `https://api.github.com/repos/${GITHUB_CONFIG.owner}/${GITHUB_CONFIG.repo}/contents/${skillPath}?ref=${GITHUB_CONFIG.branch}`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
@@ -489,7 +506,30 @@ async function fetchSkillDirectoryContents(skillPath: string): Promise<GitHubTre
|
||||
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json() as Promise<GitHubTreeEntry[]>;
|
||||
const contents = await response.json();
|
||||
|
||||
// Runtime validation: must be an array
|
||||
if (!Array.isArray(contents)) {
|
||||
throw new Error(`Unexpected GitHub API response for directory: ${skillPath}`);
|
||||
}
|
||||
|
||||
// Normalize and validate each entry
|
||||
return contents.map((entry: GitHubContentEntry) => {
|
||||
if (!entry.name || !entry.path || !entry.type) {
|
||||
throw new Error(`Invalid GitHub API entry: ${JSON.stringify(entry)}`);
|
||||
}
|
||||
|
||||
// Normalize GitHub type to internal type
|
||||
const normalizedType: 'blob' | 'tree' = entry.type === 'dir' ? 'tree' : 'blob';
|
||||
|
||||
return {
|
||||
path: entry.path,
|
||||
type: normalizedType,
|
||||
sha: entry.sha,
|
||||
size: entry.size,
|
||||
url: entry.url,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user