Files
trueref/docs/features/TRUEREF-0014.md
2026-03-22 17:08:15 +01:00

4.7 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