@defai.digital/ax-schemas
v4.4.12
Published
Zod schema definitions and TypeScript types for AX CLI ecosystem - Shared validation schemas for config, API, tools, and MCP protocols.
Downloads
4,963
Maintainers
Readme
@ax-cli/schemas
Single Source of Truth (SSOT) Type System for AX CLI.
This package provides centralized Zod schemas, brand types, and enums for the entire ax-cli ecosystem, ensuring type safety and runtime validation across all modules.
Installation
npm install @ax-cli/schemasWhat is Single Source of Truth (SSOT)?
Single Source of Truth (SSOT): All modules (API / MCP / Usage) share the same type definition contract.
Before @ax-cli/schemas, type definitions were scattered across the codebase, leading to:
- Duplicated schema definitions
- Inconsistent validation logic
- Type mismatches between modules
- High refactoring costs
Architecture: Before vs. After
BEFORE (Distributed, Inconsistent):
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ API Handler │ │ MCP Adapter │ │ Usage Module │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ • Own schemas │ │ • Own schemas │ │ • Own schemas │
│ • Own types │ │ • Own types │ │ • Own types │
│ • Own enums │ │ • Own enums │ │ • Own enums │
└─────────────────┘ └─────────────────┘ └─────────────────┘
❌ ❌ ❌
Duplicated Duplicated Duplicated
Diverges over time Diverges over time Diverges over timeAFTER (Centralized, Consistent):
┌──────────────────────────────┐
│ @ax-cli/schemas │
├──────────────────────────────┤
│ • Brand Types (ID safety) │
│ • Centralized Enums │
│ • Zod Schemas (validation) │
│ • TypeScript Types │
└───────────────┬──────────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ API Handler │ │ MCP Adapter │ │Usage Module │
├─────────────┤ ├─────────────┤ ├─────────────┤
│ import {...}│ │ import {...}│ │ import {...}│
│ from │ │ from │ │ from │
│ '@ax-cli/ │ │ '@ax-cli/ │ │ '@ax-cli/ │
│ schemas' │ │ schemas' │ │ schemas' │
└─────────────┘ └─────────────┘ └─────────────┘
✅ ✅ ✅
Same contract Same contract Same contractBenefits
1. Zero Divergence
All modules consume the same source - when you update ModelId schema, all consumers get the update automatically.
2. Reduced Refactoring Cost
Change once in @ax-cli/schemas, propagate everywhere. No need to hunt down all duplicate definitions.
3. Compile-Time Safety TypeScript catches type mismatches across module boundaries:
// API handler expects ModelId
function handleRequest(model: ModelId) { ... }
// MCP adapter provides ModelId (same type!)
const mcpModel = ModelIdSchema.parse(input);
handleRequest(mcpModel); // ✅ Type-safe!
// Usage module also uses ModelId (same type!)
const usageModel = getModelFromDB(); // Returns ModelId
handleRequest(usageModel); // ✅ Type-safe!4. Runtime Validation Consistency All modules use the same Zod schemas, ensuring consistent validation rules across API boundaries, MCP adapters, and usage tracking.
Features
- Brand Types: Compile-time nominal typing for ID types to prevent mixing
- Centralized Enums: Single source of truth for all enum values
- Zod Schemas: Runtime validation with TypeScript type inference
- Zero Runtime Cost: Brand types are compile-time only (erased during compilation)
- 100% Test Coverage: Comprehensive test suite (123 tests)
Quick Start
Using Brand Types
import { ModelId, ModelIdSchema } from '@ax-cli/schemas';
// ✅ Safe: Runtime validation + branding
const modelId = ModelIdSchema.parse('glm-4.6');
// ✅ Type-safe function signatures
function getModelConfig(id: ModelId) {
// TypeScript prevents passing other ID types here
}
// ❌ Compile error: Can't pass plain string
getModelConfig('glm-4.6'); // TypeScript error!
// ✅ Correct: Parse first
getModelConfig(ModelIdSchema.parse('glm-4.6'));Using Enums
import { MessageRoleEnum, type MessageRole } from '@ax-cli/schemas';
// ✅ Use in Zod schemas
const MessageSchema = z.object({
role: MessageRoleEnum,
content: z.string(),
});
// ✅ Use type for function signatures
function handleMessage(role: MessageRole, content: string) {
// role is typed as 'system' | 'user' | 'assistant' | 'tool'
}API Reference
Brand Types
All ID types are branded to prevent accidental mixing:
import {
ApiResponseId,
ToolCallId,
ModelId,
MCPServerId,
// ... and their corresponding schemas
ApiResponseIdSchema,
ToolCallIdSchema,
ModelIdSchema,
MCPServerIdSchema,
} from '@ax-cli/schemas';Available Brand Types:
ApiResponseId/ApiResponseIdSchemaToolCallId/ToolCallIdSchemaModelId/ModelIdSchemaTenantId/TenantIdSchemaApiKeyId/ApiKeyIdSchemaMCPServerId/MCPServerIdSchemaUsageRecordId/UsageRecordIdSchemaPlanId/PlanIdSchemaSessionId/SessionIdSchemaRequestId/RequestIdSchema
Enums
import {
MessageRoleEnum,
FinishReasonEnum,
TransportEnum,
EditorCommandEnum,
} from '@ax-cli/schemas';MessageRoleEnum: 'system' | 'user' | 'assistant' | 'tool'
FinishReasonEnum: 'stop' | 'length' | 'tool_calls' | 'content_filter'
TransportEnum: 'stdio' | 'http' | 'sse'
EditorCommandEnum: 'view' | 'create' | 'str_replace' | 'insert' | 'undo_edit'
Brand Type Utilities
import {
brand,
unbrand,
isBranded,
createBrandFactory,
type Brand,
} from '@ax-cli/schemas';
// Create custom brand types
const MyIdSchema = z.string().transform((val) => brand<string, 'MyId'>(val));
type MyId = Brand<string, 'MyId'>;
// Remove branding (use sparingly!)
const plainString = unbrand(brandedId);
// Check if value is branded
if (isBranded(value, 'ModelId')) {
// TypeScript knows this is a ModelId
}Security Best Practices
CRITICAL: Brand Types Are Compile-Time Only
Brand types provide ZERO runtime validation. You MUST validate all external inputs at system boundaries.
// ❌ UNSAFE: No runtime validation!
const tenantId = userInput as TenantId; // DON'T DO THIS!
// ✅ SAFE: Validates with Zod
const result = TenantIdSchema.safeParse(userInput);
if (result.success) {
const tenantId = result.data; // Type-safe AND runtime-safe
}When to Validate
Always validate at these boundaries:
- API boundaries: HTTP requests, MCP inputs
- File I/O: Reading configs, user settings
- Database queries: WHERE clauses with user IDs
- Environment variables
- Command-line arguments
Safe Validation Pattern
import { ModelIdSchema, type ModelId } from '@ax-cli/schemas';
function updateModel(modelName: string): void {
// ✅ Validate at boundary
const result = ModelIdSchema.safeParse(modelName);
if (!result.success) {
throw new Error(`Invalid model ID: ${result.error.message}`);
}
// Now we have a branded ModelId
const modelId: ModelId = result.data;
// Use in type-safe functions
processModel(modelId);
}
function processModel(id: ModelId): void {
// TypeScript ensures only ModelId can be passed here
// No accidental mixing with other ID types!
}Migration Guide
Adding Brand Types to Existing Code
Step 1: Import the schema
import { ModelIdSchema } from '@ax-cli/schemas';Step 2: Use .parse() at object creation boundaries
// Before
const config = {
model: 'glm-4.6', // Plain string
};
// After
const config = {
model: ModelIdSchema.parse('glm-4.6'), // Branded ModelId
};Step 3: Update function signatures
// Before
function getConfig(model: string) { }
// After
function getConfig(model: ModelId) { }Step 4: Fix TypeScript errors by adding .parse() at call sites
// Before
getConfig('glm-4.6'); // Now causes TypeScript error
// After
getConfig(ModelIdSchema.parse('glm-4.6')); // Type-safe!Default Values
For constants and defaults, parse at definition:
import { ModelIdSchema } from '@ax-cli/schemas';
const DEFAULT_CONFIG = {
model: ModelIdSchema.parse('glm-4.6'),
// ... other fields
};Performance
- Brand types: Zero runtime cost (compile-time only)
- Zod validation: ~1-2μs per validation (negligible)
- Test performance: 123 tests in <20ms
Testing
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Type checking
npm run typecheck
# Build
npm run buildArchitecture
packages/schemas/
├── src/
│ ├── index.ts # Public API
│ ├── public/
│ │ └── core/
│ │ ├── brand-types.ts # Brand type utilities
│ │ ├── enums.ts # Centralized enums
│ │ └── id-types.ts # ID brand types
│ └── __tests__/
│ ├── brand-types.test.ts # 40 tests
│ ├── enums.test.ts # 31 tests
│ └── id-types.test.ts # 52 tests
└── dist/ # Compiled outputVersion
Current version: 1.0.0
License
MIT
Related Packages
@ax-cli/schemas(this package): Type system foundationax-cli: Main CLI application
Contributing
When adding new brand types or enums:
- Add implementation to
src/public/core/ - Export from
src/index.ts - Add comprehensive tests
- Update this README
- Run
npm testandnpm run typecheck
Support
For issues and questions, please open an issue in the main ax-cli repository.
