All files / src/entities/container_registry schema-readonly.ts

100% Statements 12/12
100% Branches 0/0
100% Functions 0/0
100% Lines 12/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 9017x 17x                       17x         17x         17x       17x             17x           17x                     17x               17x                 17x                 17x                        
import { z } from 'zod';
import { requiredId } from '../utils';
 
// ============================================================================
// browse_registry - CQRS Query Tool (discriminated union schema)
// Actions: list_repositories, get_repository, list_tags, get_tag
// Backed by the GitLab GraphQL Container Registry API. Tags fold in as actions
// rather than a separate CQRS pair. Gated behind USE_REGISTRY. Free tier.
// ============================================================================
 
// --- Shared fields ---
// list_repositories resolves the project via the GraphQL `project(fullPath:)`
// field, so it needs the project PATH (numeric IDs are not accepted there).
const projectPathField = requiredId.describe(
  "Project full path (e.g., 'my-group/my-project') - required by the GraphQL project lookup",
);
// Other actions address the repository directly by its numeric ID (as returned
// by list_repositories); it is expanded to a global ID internally.
const repositoryIdField = z.coerce
  .number()
  .int()
  .positive()
  .describe('Numeric ID of the container repository (from list_repositories)');
const tagNameField = z
  .string()
  .min(1)
  .describe('Container image tag name (e.g., "latest", "v1.2.0")');
const firstField = z.coerce
  .number()
  .int()
  .positive()
  .max(100)
  .optional()
  .describe('Max number of items to return (cursor pagination, default 20, max 100)');
const afterField = z
  .string()
  .optional()
  .describe('Cursor for the next page (endCursor from a previous response)');
 
// --- Action: list_repositories ---
const ListRepositoriesSchema = z.object({
  action: z
    .literal('list_repositories')
    .describe("List a project's container registry repositories"),
  project_id: projectPathField,
  name: z.string().optional().describe('Filter repositories by name (substring match)'),
  first: firstField,
  after: afterField,
});
 
// --- Action: get_repository ---
const GetRepositorySchema = z.object({
  action: z
    .literal('get_repository')
    .describe('Get a single container repository by its numeric ID'),
  repository_id: repositoryIdField,
});
 
// --- Action: list_tags ---
const ListTagsSchema = z.object({
  action: z.literal('list_tags').describe('List tags of a container repository'),
  repository_id: repositoryIdField,
  name: z.string().optional().describe('Filter tags by name (substring match)'),
  first: firstField,
  after: afterField,
});
 
// --- Action: get_tag ---
const GetTagSchema = z.object({
  action: z
    .literal('get_tag')
    .describe('Get a single tag with its manifest digest, size, and timestamps'),
  repository_id: repositoryIdField,
  tag_name: tagNameField,
});
 
// --- Discriminated union combining all actions ---
export const BrowseRegistrySchema = z.discriminatedUnion('action', [
  ListRepositoriesSchema,
  GetRepositorySchema,
  ListTagsSchema,
  GetTagSchema,
]);
 
// ============================================================================
// Type exports
// ============================================================================
 
export type BrowseRegistryInput = z.infer<typeof BrowseRegistrySchema>;