Initial commit: restructure to flat layout with ui/ and web/ at root
This commit is contained in:
85
web/server/lib/skills-catalog/source.js
Normal file
85
web/server/lib/skills-catalog/source.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const GITHUB_HOST = 'github.com';
|
||||
|
||||
function normalizeGitHubOwnerRepo(owner, repo) {
|
||||
const normalizedOwner = String(owner || '').trim();
|
||||
const normalizedRepo = String(repo || '').trim().replace(/\.git$/i, '');
|
||||
if (!normalizedOwner || !normalizedRepo) {
|
||||
return null;
|
||||
}
|
||||
return { owner: normalizedOwner, repo: normalizedRepo };
|
||||
}
|
||||
|
||||
export function parseSkillRepoSource(input, options = {}) {
|
||||
const raw = typeof input === 'string' ? input.trim() : '';
|
||||
if (!raw) {
|
||||
return { ok: false, error: { kind: 'invalidSource', message: 'Repository source is required' } };
|
||||
}
|
||||
|
||||
const explicitSubpath = typeof options.subpath === 'string' && options.subpath.trim() ? options.subpath.trim() : null;
|
||||
|
||||
// SSH URL: git@github.com:owner/repo(.git)
|
||||
const sshMatch = raw.match(/^git@github\.com:([^/\s]+)\/([^\s#]+)$/i);
|
||||
if (sshMatch) {
|
||||
const parsed = normalizeGitHubOwnerRepo(sshMatch[1], sshMatch[2]);
|
||||
if (!parsed) {
|
||||
return { ok: false, error: { kind: 'invalidSource', message: 'Invalid SSH repository URL' } };
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
host: GITHUB_HOST,
|
||||
owner: parsed.owner,
|
||||
repo: parsed.repo,
|
||||
cloneUrlSsh: `git@github.com:${parsed.owner}/${parsed.repo}.git`,
|
||||
cloneUrlHttps: `https://github.com/${parsed.owner}/${parsed.repo}.git`,
|
||||
// For SSH URLs, subpath is only accepted via options.subpath
|
||||
effectiveSubpath: explicitSubpath,
|
||||
normalizedRepo: `${parsed.owner}/${parsed.repo}`,
|
||||
};
|
||||
}
|
||||
|
||||
// HTTPS URL: https://github.com/owner/repo(.git)
|
||||
const httpsMatch = raw.match(/^https?:\/\/github\.com\/([^/\s]+)\/([^\s#]+)$/i);
|
||||
if (httpsMatch) {
|
||||
const parsed = normalizeGitHubOwnerRepo(httpsMatch[1], httpsMatch[2]);
|
||||
if (!parsed) {
|
||||
return { ok: false, error: { kind: 'invalidSource', message: 'Invalid HTTPS repository URL' } };
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
host: GITHUB_HOST,
|
||||
owner: parsed.owner,
|
||||
repo: parsed.repo,
|
||||
cloneUrlSsh: `git@github.com:${parsed.owner}/${parsed.repo}.git`,
|
||||
cloneUrlHttps: `https://github.com/${parsed.owner}/${parsed.repo}.git`,
|
||||
effectiveSubpath: explicitSubpath,
|
||||
normalizedRepo: `${parsed.owner}/${parsed.repo}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Shorthand: owner/repo[/subpath...]
|
||||
const shorthandMatch = raw.match(/^([^/\s]+)\/([^/\s]+)(?:\/(.+))?$/);
|
||||
if (shorthandMatch) {
|
||||
const parsed = normalizeGitHubOwnerRepo(shorthandMatch[1], shorthandMatch[2]);
|
||||
if (!parsed) {
|
||||
return { ok: false, error: { kind: 'invalidSource', message: 'Invalid repository source' } };
|
||||
}
|
||||
|
||||
const shorthandSubpath = typeof shorthandMatch[3] === 'string' && shorthandMatch[3].trim() ? shorthandMatch[3].trim() : null;
|
||||
const effectiveSubpath = explicitSubpath || shorthandSubpath;
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
host: GITHUB_HOST,
|
||||
owner: parsed.owner,
|
||||
repo: parsed.repo,
|
||||
cloneUrlSsh: `git@github.com:${parsed.owner}/${parsed.repo}.git`,
|
||||
cloneUrlHttps: `https://github.com/${parsed.owner}/${parsed.repo}.git`,
|
||||
effectiveSubpath,
|
||||
normalizedRepo: `${parsed.owner}/${parsed.repo}`,
|
||||
};
|
||||
}
|
||||
|
||||
return { ok: false, error: { kind: 'invalidSource', message: 'Unsupported repository source format' } };
|
||||
}
|
||||
Reference in New Issue
Block a user