@dexwox-labs/a2a-core
Version:
Core types, validation and telemetry for Google's Agent-to-Agent (A2A) protocol - shared foundation for client and server implementations
448 lines • 15.3 kB
JavaScript
"use strict";
/**
* @module Validators
* @description Schema validation utilities for A2A protocol types
*
* This module provides schema definitions and validation functions for the A2A protocol.
* It uses Zod for runtime type validation and provides helper functions for validating
* various protocol objects.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.schemas = exports.isPushNotificationConfig = exports.isAgentCard = exports.isTask = exports.isMessage = exports.validateDiscoverResponse = exports.validateDiscoverRequest = exports.validatePushNotificationConfig = exports.validateAgentCard = exports.validateTask = exports.validateMessage = void 0;
exports.formatValidationError = formatValidationError;
exports.validate = validate;
const zod_1 = require("zod");
const a2a_protocol_1 = require("../types/a2a-protocol");
// Extended schemas with additional validation
const TaskTransitionSchema = zod_1.z.object({
from: a2a_protocol_1.TaskStateSchema,
to: a2a_protocol_1.TaskStateSchema,
timestamp: zod_1.z.string().datetime(),
reason: zod_1.z.string().optional(),
});
// Define the message part schemas individually
const TextMessagePartSchema = zod_1.z.object({
type: zod_1.z.literal('text'),
content: zod_1.z.string(),
format: zod_1.z.enum(['plain', 'markdown']).default('plain'),
});
const FileMessagePartSchema = zod_1.z.object({
type: zod_1.z.literal('file'),
content: zod_1.z.union([zod_1.z.string(), zod_1.z.instanceof(Uint8Array)]),
mimeType: zod_1.z.string(),
name: zod_1.z.string(),
size: zod_1.z.number().optional(),
});
const DataMessagePartSchema = zod_1.z.object({
type: zod_1.z.literal('data'),
content: zod_1.z.record(zod_1.z.any()),
schema: zod_1.z.string().optional(),
});
const HeartbeatMessagePartSchema = zod_1.z.object({
type: zod_1.z.literal('heartbeat'),
content: zod_1.z.string(),
format: zod_1.z.literal('plain').default('plain'),
});
// Create the union type
const StrictMessagePartSchema = zod_1.z.union([
TextMessagePartSchema,
FileMessagePartSchema,
DataMessagePartSchema,
HeartbeatMessagePartSchema,
]);
const MessageSchema = zod_1.z.object({
parts: zod_1.z.array(StrictMessagePartSchema).min(1, 'At least one message part is required'),
taskId: zod_1.z.string().uuid().optional(),
contextId: zod_1.z.string().uuid().optional(),
});
const MessageSendConfigurationSchema = zod_1.z.object({
priority: zod_1.z.number().int().min(0).max(100).optional(),
timeout: zod_1.z.number().int().positive().optional(),
metadata: zod_1.z.record(zod_1.z.unknown()).optional(),
});
const TaskSchema = zod_1.z.object({
id: zod_1.z.string().uuid(),
name: zod_1.z.string().min(1, 'Task name is required'),
description: zod_1.z.string().optional(),
status: a2a_protocol_1.TaskStateSchema,
agentId: zod_1.z.string().optional(),
parts: zod_1.z.array(StrictMessagePartSchema).optional(),
expectedParts: zod_1.z.number().int().positive().optional(),
artifacts: zod_1.z.array(a2a_protocol_1.ArtifactSchema).optional(),
transitions: zod_1.z.array(TaskTransitionSchema).optional(),
createdAt: zod_1.z.string().datetime(),
updatedAt: zod_1.z.string().datetime(),
error: a2a_protocol_1.A2AErrorSchema.optional(),
metadata: zod_1.z.record(zod_1.z.unknown()).optional(),
contextId: zod_1.z.string().uuid().optional(),
inputSchema: zod_1.z.record(zod_1.z.any()).optional(),
outputSchema: zod_1.z.record(zod_1.z.any()).optional(),
input: zod_1.z.record(zod_1.z.any()).optional(),
output: zod_1.z.record(zod_1.z.any()).optional(),
});
const AgentCardSchema = zod_1.z.object({
id: zod_1.z.string().uuid(),
name: zod_1.z.string().min(1, 'Agent name is required'),
capabilities: zod_1.z.array(zod_1.z.string().min(1, 'Capability cannot be empty')),
endpoint: zod_1.z.string().url('Endpoint must be a valid URL'),
metadata: zod_1.z.record(zod_1.z.unknown()).optional(),
});
const PushNotificationConfigSchema = zod_1.z.object({
enabled: zod_1.z.boolean(),
endpoint: zod_1.z.string().url('Endpoint must be a valid URL').optional(),
authToken: zod_1.z.string().optional(),
events: zod_1.z.array(zod_1.z.string().min(1, 'Event name cannot be empty')),
metadata: zod_1.z.record(zod_1.z.unknown()).optional(),
});
const DiscoverRequestSchema = zod_1.z.object({
id: zod_1.z.string().min(1, 'ID is required'),
method: zod_1.z.literal('discover'),
params: zod_1.z.record(zod_1.z.unknown()).optional(),
jsonrpc: zod_1.z.literal('2.0').optional(),
});
const DiscoverResponseSchema = zod_1.z.object({
id: zod_1.z.string(),
result: zod_1.z.array(AgentCardSchema),
error: a2a_protocol_1.A2AErrorSchema.optional(),
});
/**
* Validation functions for A2A protocol objects
*
* These functions validate objects against their respective schemas and return
* a SafeParseReturnType that includes either the validated data or validation errors.
*/
/**
* Validates a message object against the Message schema
*
* @param data - The data to validate
* @returns A SafeParseReturnType containing either the validated Message or validation errors
*
* @example
* ```typescript
* const result = validateMessage({
* parts: [{ type: 'text', content: 'Hello, world!' }]
* });
*
* if (result.success) {
* // Use the validated message
* console.log('Valid message:', result.data);
* } else {
* // Handle validation errors
* console.error('Invalid message:', result.error);
* }
* ```
*/
const validateMessage = (data) => MessageSchema.safeParse(data);
exports.validateMessage = validateMessage;
/**
* Validates a task object against the Task schema
*
* @param data - The data to validate
* @returns A SafeParseReturnType containing either the validated Task or validation errors
*
* @example
* ```typescript
* const result = validateTask({
* id: '123e4567-e89b-12d3-a456-426614174000',
* name: 'Process Data',
* status: 'submitted',
* createdAt: new Date().toISOString(),
* updatedAt: new Date().toISOString()
* });
*
* if (result.success) {
* // Use the validated task
* console.log('Valid task:', result.data);
* } else {
* // Handle validation errors
* console.error('Invalid task:', result.error);
* }
* ```
*/
const validateTask = (data) => TaskSchema.safeParse(data);
exports.validateTask = validateTask;
/**
* Validates an agent card object against the AgentCard schema
*
* @param data - The data to validate
* @returns A SafeParseReturnType containing either the validated AgentCard or validation errors
*
* @example
* ```typescript
* const result = validateAgentCard({
* id: '123e4567-e89b-12d3-a456-426614174000',
* name: 'Weather Agent',
* capabilities: ['weather-forecasting', 'location-search'],
* endpoint: 'https://example.com/agents/weather'
* });
*
* if (result.success) {
* // Use the validated agent card
* console.log('Valid agent card:', result.data);
* } else {
* // Handle validation errors
* console.error('Invalid agent card:', result.error);
* }
* ```
*/
const validateAgentCard = (data) => AgentCardSchema.safeParse(data);
exports.validateAgentCard = validateAgentCard;
/**
* Validates a push notification configuration against the PushNotificationConfig schema
*
* @param data - The data to validate
* @returns A SafeParseReturnType containing either the validated PushNotificationConfig or validation errors
*
* @example
* ```typescript
* const result = validatePushNotificationConfig({
* enabled: true,
* endpoint: 'https://example.com/webhooks/a2a',
* authToken: 'secret-token',
* events: ['taskCompleted', 'taskFailed']
* });
*
* if (result.success) {
* // Use the validated config
* console.log('Valid push config:', result.data);
* } else {
* // Handle validation errors
* console.error('Invalid push config:', result.error);
* }
* ```
*/
const validatePushNotificationConfig = (data) => PushNotificationConfigSchema.safeParse(data);
exports.validatePushNotificationConfig = validatePushNotificationConfig;
/**
* Validates a discover request against the DiscoverRequest schema
*
* @param data - The data to validate
* @returns A SafeParseReturnType containing either the validated DiscoverRequest or validation errors
*
* @example
* ```typescript
* const result = validateDiscoverRequest({
* id: '1',
* method: 'discover',
* params: { capability: 'weather-forecasting' },
* jsonrpc: '2.0'
* });
*
* if (result.success) {
* // Use the validated request
* console.log('Valid discover request:', result.data);
* } else {
* // Handle validation errors
* console.error('Invalid discover request:', result.error);
* }
* ```
*/
const validateDiscoverRequest = (data) => DiscoverRequestSchema.safeParse(data);
exports.validateDiscoverRequest = validateDiscoverRequest;
/**
* Validates a discover response against the DiscoverResponse schema
*
* @param data - The data to validate
* @returns A SafeParseReturnType containing either the validated DiscoverResponse or validation errors
*
* @example
* ```typescript
* const result = validateDiscoverResponse({
* id: '1',
* result: [
* {
* id: '123e4567-e89b-12d3-a456-426614174000',
* name: 'Weather Agent',
* capabilities: ['weather-forecasting'],
* endpoint: 'https://example.com/agents/weather'
* }
* ]
* });
*
* if (result.success) {
* // Use the validated response
* console.log('Valid discover response:', result.data);
* } else {
* // Handle validation errors
* console.error('Invalid discover response:', result.error);
* }
* ```
*/
const validateDiscoverResponse = (data) => DiscoverResponseSchema.safeParse(data);
exports.validateDiscoverResponse = validateDiscoverResponse;
/**
* Type guards for A2A protocol objects
*
* These functions check if an object conforms to a specific schema and narrow
* its type if it does. They return a boolean indicating whether the object is
* of the specified type.
*/
/**
* Checks if the provided data is a valid Message
*
* @param data - The data to check
* @returns True if the data is a valid Message, false otherwise
*
* @example
* ```typescript
* const data = { parts: [{ type: 'text', content: 'Hello' }] };
*
* if (isMessage(data)) {
* // TypeScript now knows that data is a Message
* console.log('Message parts:', data.parts.length);
* }
* ```
*/
const isMessage = (data) => MessageSchema.safeParse(data).success;
exports.isMessage = isMessage;
/**
* Checks if the provided data is a valid Task
*
* @param data - The data to check
* @returns True if the data is a valid Task, false otherwise
*
* @example
* ```typescript
* const data = getTaskFromSomewhere();
*
* if (isTask(data)) {
* // TypeScript now knows that data is a Task
* console.log('Task status:', data.status);
* }
* ```
*/
const isTask = (data) => TaskSchema.safeParse(data).success;
exports.isTask = isTask;
/**
* Checks if the provided data is a valid AgentCard
*
* @param data - The data to check
* @returns True if the data is a valid AgentCard, false otherwise
*
* @example
* ```typescript
* const data = getAgentFromSomewhere();
*
* if (isAgentCard(data)) {
* // TypeScript now knows that data is an AgentCard
* console.log('Agent capabilities:', data.capabilities);
* }
* ```
*/
const isAgentCard = (data) => AgentCardSchema.safeParse(data).success;
exports.isAgentCard = isAgentCard;
/**
* Checks if the provided data is a valid PushNotificationConfig
*
* @param data - The data to check
* @returns True if the data is a valid PushNotificationConfig, false otherwise
*
* @example
* ```typescript
* const data = getConfigFromSomewhere();
*
* if (isPushNotificationConfig(data)) {
* // TypeScript now knows that data is a PushNotificationConfig
* console.log('Push notifications enabled:', data.enabled);
* }
* ```
*/
const isPushNotificationConfig = (data) => PushNotificationConfigSchema.safeParse(data).success;
exports.isPushNotificationConfig = isPushNotificationConfig;
/**
* Formats a Zod validation error into a human-readable string
*
* This function takes a Zod error object and converts it into a string
* representation, with each issue formatted as "path: message" and joined
* with semicolons.
*
* @param error - The Zod error to format
* @returns A formatted error string
*
* @example
* ```typescript
* const result = validateTask(invalidTask);
*
* if (!result.success) {
* const errorMessage = formatValidationError(result.error);
* console.error('Validation failed:', errorMessage);
* // Example output: "name: Required; status: Invalid enum value"
* }
* ```
*/
function formatValidationError(error) {
return error.issues
.map(issue => {
const path = issue.path.join('.');
return path ? `${path}: ${issue.message}` : issue.message;
})
.join('; ');
}
/**
* Decorator for validating method parameters against a schema
*
* This decorator can be applied to methods to automatically validate their
* first parameter against the provided schema. If validation fails, an error
* is thrown with details about the validation issues.
*
* @param schema - The Zod schema to validate against
* @returns A method decorator function
*
* @example
* ```typescript
* class MessageService {
* @validate(MessageSchema)
* sendMessage(message: Message) {
* // This code only runs if message passes validation
* console.log('Sending message:', message);
* }
* }
*
* const service = new MessageService();
*
* // This will throw an error because the message is invalid
* service.sendMessage({ invalid: 'data' });
* ```
*/
function validate(schema) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
const result = schema.safeParse(args[0]);
if (!result.success) {
throw new Error(`Validation failed: ${formatValidationError(result.error)}`);
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
/**
* Collection of all schema definitions for A2A protocol objects
*
* This object provides access to all the Zod schemas defined in this module,
* allowing them to be used directly for validation or type inference.
*
* @example
* ```typescript
* // Use a schema directly for validation
* const result = schemas.Message.safeParse(data);
*
* // Create a type from a schema
* type MessageType = z.infer<typeof schemas.Message>;
* ```
*/
exports.schemas = {
Message: MessageSchema,
Task: TaskSchema,
AgentCard: AgentCardSchema,
PushNotificationConfig: PushNotificationConfigSchema,
DiscoverRequest: DiscoverRequestSchema,
DiscoverResponse: DiscoverResponseSchema,
MessagePart: a2a_protocol_1.MessagePartSchema,
Artifact: a2a_protocol_1.ArtifactSchema,
A2AError: a2a_protocol_1.A2AErrorSchema,
TaskTransition: TaskTransitionSchema,
MessageSendConfiguration: MessageSendConfigurationSchema,
};
//# sourceMappingURL=validators.js.map