320 lines
9.7 KiB
TypeScript
320 lines
9.7 KiB
TypeScript
/**
|
|
* Zod schemas describing the rpki_query_service API contract.
|
|
*
|
|
* Schemas are intentionally `.passthrough()` so additive backend changes do
|
|
* not break the UI; fields the UI depends on are typed strictly.
|
|
*/
|
|
import { z } from "zod";
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Records */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
export const runCountsSchema = z
|
|
.object({
|
|
publicationPoints: z.number().optional(),
|
|
objects: z.number().optional(),
|
|
freshObjects: z.number().optional(),
|
|
cachedObjects: z.number().optional(),
|
|
rejectedObjects: z.number().optional(),
|
|
freshRejectedObjects: z.number().optional(),
|
|
cachedRejectedObjects: z.number().optional(),
|
|
trustAnchors: z.number().optional(),
|
|
vrps: z.number().optional(),
|
|
aspas: z.number().optional(),
|
|
warnings: z.number().optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const runRecordSchema = z
|
|
.object({
|
|
schemaVersion: z.number().optional(),
|
|
runId: z.string(),
|
|
runSeq: z.number().nullish(),
|
|
runDir: z.string().optional(),
|
|
validationTime: z.string().nullish(),
|
|
syncMode: z.string().nullish(),
|
|
startedAt: z.string().nullish(),
|
|
finishedAt: z.string().nullish(),
|
|
wallMs: z.number().nullish(),
|
|
maxRssKb: z.number().nullish(),
|
|
artifactPaths: z.record(z.string()).optional(),
|
|
counts: runCountsSchema.optional(),
|
|
indexStatus: z.string().optional(),
|
|
indexError: z.string().nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const repositoryRecordSchema = z
|
|
.object({
|
|
repoId: z.string(),
|
|
uri: z.string(),
|
|
host: z.string(),
|
|
transport: z.string().nullish(),
|
|
publicationPoints: z.number().nullish(),
|
|
objects: z.number().nullish(),
|
|
rejectedObjects: z.number().nullish(),
|
|
downloadBytes: z.number().nullish(),
|
|
syncDurationMsTotal: z.number().nullish(),
|
|
phases: z.record(z.number()).optional(),
|
|
terminalStates: z.record(z.number()).optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const publicationPointRecordSchema = z
|
|
.object({
|
|
ppId: z.string(),
|
|
repoId: z.string(),
|
|
nodeId: z.number().nullish(),
|
|
parentNodeId: z.number().nullish(),
|
|
rsyncBaseUri: z.string().nullish(),
|
|
manifestRsyncUri: z.string().nullish(),
|
|
publicationPointRsyncUri: z.string().nullish(),
|
|
rrdpNotificationUri: z.string().nullish(),
|
|
source: z.string().nullish(),
|
|
repoSyncSource: z.string().nullish(),
|
|
repoSyncPhase: z.string().nullish(),
|
|
repoSyncDurationMs: z.number().nullish(),
|
|
repoSyncError: z.string().nullish(),
|
|
repoTerminalState: z.string().nullish(),
|
|
thisUpdate: z.string().nullish(),
|
|
nextUpdate: z.string().nullish(),
|
|
verifiedAt: z.string().nullish(),
|
|
objects: z.number().nullish(),
|
|
rejectedObjects: z.number().nullish(),
|
|
warnings: z.number().nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const objectInstanceRecordSchema = z
|
|
.object({
|
|
objectInstanceId: z.string(),
|
|
uri: z.string(),
|
|
uriHash: z.string().optional(),
|
|
sha256: z.string().optional(),
|
|
objectType: z.string(),
|
|
result: z.string().nullish(),
|
|
detailSummary: z.string().nullish(),
|
|
repoId: z.string(),
|
|
ppId: z.string(),
|
|
sourceSection: z.string().nullish(),
|
|
rejected: z.boolean().optional(),
|
|
rejectReason: z.string().nullish(),
|
|
sizeBytes: z.number().nullish(),
|
|
rawAvailable: z.boolean().optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const chainEdgeRecordSchema = z
|
|
.object({
|
|
relation: z.string(),
|
|
fromUri: z.string().nullish(),
|
|
toUri: z.string().nullish(),
|
|
toObjectInstanceId: z.string().nullish(),
|
|
toSha256: z.string().nullish(),
|
|
status: z.string().nullish(),
|
|
evidence: z.unknown().optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
/** Sparse record returned by /objects/by-uri (index hit, no lazy scan). */
|
|
export const objectUriIndexRecordSchema = z
|
|
.object({
|
|
uri: z.string(),
|
|
sha256: z.string(),
|
|
objectInstanceId: z.string(),
|
|
repoId: z.string(),
|
|
ppId: z.string(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const exportJobRecordSchema = z
|
|
.object({
|
|
jobId: z.string(),
|
|
runId: z.string(),
|
|
scope: z.string(),
|
|
repoId: z.string().nullish(),
|
|
ppId: z.string().nullish(),
|
|
status: z.string(),
|
|
createdAt: z.string().nullish(),
|
|
finishedAt: z.string().nullish(),
|
|
outputPath: z.string().nullish(),
|
|
objectCount: z.number().nullish(),
|
|
bytesWritten: z.number().nullish(),
|
|
error: z.string().nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const projectionRecordSchema = z
|
|
.object({
|
|
schemaVersion: z.number().optional(),
|
|
sha256: z.string().optional(),
|
|
objectType: z.string().optional(),
|
|
parseStatus: z.string().optional(),
|
|
errorSummary: z.string().nullish(),
|
|
projection: z.record(z.unknown()).nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const validationIssueSchema = z
|
|
.object({
|
|
severity: z.string().nullish(),
|
|
reasonCode: z.string().nullish(),
|
|
reasonText: z.string().nullish(),
|
|
rfcRefs: z.array(z.string()).optional(),
|
|
evidence: z.unknown().optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const validationStageSchema = z
|
|
.object({
|
|
status: z.string().nullish(),
|
|
stage: z.string().nullish(),
|
|
mode: z.string().nullish(),
|
|
projectionStatus: z.string().nullish(),
|
|
issues: z.array(validationIssueSchema).optional(),
|
|
detailSummary: z.string().nullish(),
|
|
edgesCount: z.number().nullish(),
|
|
note: z.string().nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const validationSummarySchema = z
|
|
.object({
|
|
finalStatus: z.string().nullish(),
|
|
auditResult: z.string().nullish(),
|
|
detailSummary: z.string().nullish(),
|
|
parsevalidate: validationStageSchema.nullish(),
|
|
chainvalidate: validationStageSchema.nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const explainRecordSchema = z
|
|
.object({
|
|
finalStatus: z.string().nullish(),
|
|
auditResult: z.string().nullish(),
|
|
authoritative: z.boolean().optional(),
|
|
explainMode: z.string().nullish(),
|
|
parsevalidate: validationStageSchema.nullish(),
|
|
chainvalidate: validationStageSchema.nullish(),
|
|
chainEdges: z.array(chainEdgeRecordSchema).optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const searchResultSchema = z
|
|
.object({
|
|
objects: z.array(objectInstanceRecordSchema),
|
|
repos: z.array(repositoryRecordSchema),
|
|
publicationPoints: z.array(publicationPointRecordSchema),
|
|
})
|
|
.passthrough();
|
|
|
|
export const vrpRecordSchema = z
|
|
.object({
|
|
runId: z.string(),
|
|
asn: z.number(),
|
|
prefix: z.string(),
|
|
maxLength: z.number(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const healthSchema = z
|
|
.object({
|
|
status: z.string(),
|
|
service: z.string().optional(),
|
|
version: z.number().optional(),
|
|
latestReadyRun: z.string().nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const serviceInfoSchema = z
|
|
.object({
|
|
service: z.string(),
|
|
version: z.number(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const manifestFileEntrySchema = z
|
|
.object({
|
|
fileName: z.string(),
|
|
hashHex: z.string(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const revokedCertEntrySchema = z
|
|
.object({
|
|
serialNumberHex: z.string().nullish(),
|
|
// Backend serializes CRL serials as a JSON number when they fit, else a string.
|
|
serialNumber: z.union([z.string(), z.number()]).nullish(),
|
|
revocationDate: z.string().nullish(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const runSummarySchema = z
|
|
.object({
|
|
publicationPoints: z.number().optional(),
|
|
objects: z.number().optional(),
|
|
repos: z.number().optional(),
|
|
vrps: z.number().optional(),
|
|
aspas: z.number().optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
export const statsMapSchema = z.record(z.number());
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Envelope */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
const pageSchema = z.object({
|
|
nextCursor: z.string().nullable(),
|
|
limit: z.number(),
|
|
});
|
|
|
|
const metaSchema = z
|
|
.object({
|
|
runId: z.string().nullish(),
|
|
schemaVersion: z.number().optional(),
|
|
})
|
|
.passthrough();
|
|
|
|
export function envelopeSchema<T extends z.ZodTypeAny>(dataSchema: T) {
|
|
return z
|
|
.object({
|
|
data: dataSchema,
|
|
page: pageSchema.nullish(),
|
|
meta: metaSchema.optional(),
|
|
})
|
|
.passthrough();
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Inferred types */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
export type RunCounts = z.infer<typeof runCountsSchema>;
|
|
export type RunRecord = z.infer<typeof runRecordSchema>;
|
|
export type RepositoryRecord = z.infer<typeof repositoryRecordSchema>;
|
|
export type PublicationPointRecord = z.infer<typeof publicationPointRecordSchema>;
|
|
export type ObjectInstanceRecord = z.infer<typeof objectInstanceRecordSchema>;
|
|
export type ChainEdgeRecord = z.infer<typeof chainEdgeRecordSchema>;
|
|
export type ExportJobRecord = z.infer<typeof exportJobRecordSchema>;
|
|
export type ProjectionRecord = z.infer<typeof projectionRecordSchema>;
|
|
export type ValidationIssue = z.infer<typeof validationIssueSchema>;
|
|
export type ValidationStage = z.infer<typeof validationStageSchema>;
|
|
export type ValidationSummary = z.infer<typeof validationSummarySchema>;
|
|
export type ExplainRecord = z.infer<typeof explainRecordSchema>;
|
|
export type SearchResult = z.infer<typeof searchResultSchema>;
|
|
export type VrpRecord = z.infer<typeof vrpRecordSchema>;
|
|
export type HealthStatus = z.infer<typeof healthSchema>;
|
|
export type ServiceInfo = z.infer<typeof serviceInfoSchema>;
|
|
export type ManifestFileEntry = z.infer<typeof manifestFileEntrySchema>;
|
|
export type RevokedCertEntry = z.infer<typeof revokedCertEntrySchema>;
|
|
export type RunSummary = z.infer<typeof runSummarySchema>;
|
|
|
|
export interface ListResult<T> {
|
|
items: T[];
|
|
nextCursor: string | null;
|
|
runId: string | undefined;
|
|
}
|