Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/just-cameron/tangled-cli. Automation-first CLI for Tangled.org
Something went wrong. Try again.
2.9 kB · 103 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104/** * Git utilities for parsing and validating tangled.org remote URLs */
import { isValidHandle, isValidTangledDid } from './validation.js';
export interface ParsedTangledRemote { owner: string; ownerType: 'did' | 'handle'; name: string; protocol: 'ssh' | 'https';}
// The SSH user names the account that pushes (git, or a handle), never the// repository, so any user is accepted; only the host and the path matter.const SSH_USER = '[A-Za-z0-9._-]+';const SSH_SHORTHAND = new RegExp(`^${SSH_USER}@tangled\\.org:(.+)$`);const SSH_URL = new RegExp(`^ssh://${SSH_USER}@tangled\\.org/(.+)$`);const HTTPS_URL = /^https:\/\/tangled\.org\/(.+)$/;
/** The protocol and repository path of a tangled.org remote, or null. */function remotePath(url: string): { protocol: 'ssh' | 'https'; path: string } | null { const https = url.match(HTTPS_URL); if (https) return { protocol: 'https', path: https[1] }; const ssh = url.match(SSH_URL) ?? url.match(SSH_SHORTHAND); if (ssh) return { protocol: 'ssh', path: ssh[1] }; return null;}
/** * Check if a Git remote URL is a tangled.org URL: HTTPS, or SSH as any user * (`git@tangled.org:…`, `alice.bsky.social@tangled.org:…`, `ssh://…@tangled.org/…`). */export function isTangledRemote(url: string): boolean { return remotePath(url) !== null;}
/** * Parse a tangled.org Git remote URL to extract owner and repo name * @param url - Git remote URL * @returns Parsed remote info or null if not a valid tangled URL */export function parseTangledRemote(url: string): ParsedTangledRemote | null { const remote = remotePath(url); if (!remote) return null; const { protocol } = remote; let { path } = remote;
// Remove trailing slashes path = path.replace(/\/+$/, '');
// Remove .git extension if present path = path.replace(/\.git$/, '');
// Tangled's hosted knot accepts a bare repository DID as a Git remote, // e.g. git@tangled.org:did:plc:... . This does not encode the human // owner/repo slug, but it is still a valid Tangled repository remote. if (isValidTangledDid(path)) { return { owner: path, ownerType: 'did', name: path, protocol, }; }
// Split path into owner and repo name const parts = path.split('/'); if (parts.length < 2) { return null; }
const owner = parts[0]; const name = parts[1];
// Validate that we have both parts if (!owner || !name) { return null; }
// Determine owner type based on format let ownerType: 'did' | 'handle'; if (owner.startsWith('did:plc:')) { ownerType = 'did'; // Validate DID format if (!isValidTangledDid(owner)) { return null; } } else { ownerType = 'handle'; // Validate handle format if (!isValidHandle(owner)) { return null; } }
return { owner, ownerType, name, protocol, };}