Files
trueref/docs/features/TRUEREF-0014.md
2026-03-27 02:23:01 +01:00

4.6 KiB

TRUEREF-0014 — Repository Version Management

Priority: P1 Status: Pending Depends On: TRUEREF-0003 Blocks:


Overview

Support indexing specific git tags and branches as distinct versioned snapshots of a repository. Users can query documentation for a specific version using the /owner/repo/version library ID format. Versions are registered via trueref.json's previousVersions field or manually via the API.


Acceptance Criteria

  • GET /api/v1/libs/:id/versions — list all indexed versions for a repository
  • POST /api/v1/libs/:id/versions — add a new version (tag or branch)
  • DELETE /api/v1/libs/:id/versions/:versionTag — remove a version and its snippets
  • POST /api/v1/libs/:id/versions/:versionTag/index — trigger indexing for a specific version
  • Version-specific queries: /api/v1/context?libraryId=/facebook/react/v18.3.0
  • Default branch queries: /api/v1/context?libraryId=/facebook/react (no version suffix)
  • previousVersions from trueref.json automatically registered during indexing (state: pending)
  • GitHub tag list endpoint used to validate tag existence before indexing
  • Version snippets stored with versionId FK; default branch snippets have versionId = NULL

Version ID Convention

Version ID format: {repositoryId}/{tag}

Examples:
  /facebook/react/v18.3.0
  /facebook/react/v17.0.2
  /vercel/next.js/v14.3.0-canary.1

API Endpoints

GET /api/v1/libs/:id/versions

Response 200:

{
	"versions": [
		{
			"id": "/facebook/react/v18.3.0",
			"repositoryId": "/facebook/react",
			"tag": "v18.3.0",
			"title": "React v18.3.0",
			"state": "indexed",
			"totalSnippets": 892,
			"indexedAt": "2026-03-22T10:00:00Z"
		}
	]
}

POST /api/v1/libs/:id/versions

Request body:

{
	"tag": "v18.3.0",
	"title": "React v18.3.0",
	"autoIndex": true
}

Response 201:

{
  "version": { ...RepositoryVersion },
  "job": { "id": "uuid", "status": "queued" }
}

DELETE /api/v1/libs/:id/versions/:tag

Deletes the version record and all associated documents/snippets via cascade. Response 204.

POST /api/v1/libs/:id/versions/:tag/index

Queues an indexing job for this specific version tag. Response 202 with job details.


GitHub Tag Discovery

async function listGitHubTags(
	owner: string,
	repo: string,
	token?: string
): Promise<Array<{ name: string; commit: { sha: string } }>> {
	const headers: Record<string, string> = {
		Accept: 'application/vnd.github.v3+json',
		'User-Agent': 'TrueRef/1.0'
	};
	if (token) headers['Authorization'] = `Bearer ${token}`;

	const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/tags?per_page=100`, {
		headers
	});

	if (!response.ok) throw new GitHubApiError(response.status);
	return response.json();
}

Query Routing

In the search/context endpoints, the libraryId is parsed to extract the optional version:

function resolveSearchTarget(libraryId: string): {
	repositoryId: string;
	versionId?: string;
} {
	const { repositoryId, version } = parseLibraryId(libraryId);

	if (!version) {
		// Query default branch: versionId = NULL
		return { repositoryId };
	}

	// Look up versionId from tag
	const versionRecord = db
		.prepare(`SELECT id FROM repository_versions WHERE repository_id = ? AND tag = ?`)
		.get(repositoryId, version) as { id: string } | undefined;

	if (!versionRecord) {
		throw new NotFoundError(`Version "${version}" not found for library "${repositoryId}"`);
	}

	return { repositoryId, versionId: versionRecord.id };
}

Snippets with version_id IS NULL belong to the default branch; snippets with a version_id belong to that specific version. Search queries filter by version_id = ? or version_id IS NULL accordingly.


Version Service

export class VersionService {
	constructor(private db: BetterSQLite3.Database) {}

	list(repositoryId: string): RepositoryVersion[];

	add(repositoryId: string, tag: string, title?: string): RepositoryVersion;

	remove(repositoryId: string, tag: string): void;

	getByTag(repositoryId: string, tag: string): RepositoryVersion | null;

	registerFromConfig(
		repositoryId: string,
		previousVersions: { tag: string; title: string }[]
	): RepositoryVersion[];
}

Files to Create

  • src/lib/server/services/version.service.ts
  • src/routes/api/v1/libs/[id]/versions/+server.ts — GET, POST
  • src/routes/api/v1/libs/[id]/versions/[tag]/+server.ts — DELETE
  • src/routes/api/v1/libs/[id]/versions/[tag]/index/+server.ts — POST
  • src/lib/server/crawler/github-tags.ts