Initial commit
This commit is contained in:
15
.claude-plugin/plugin.json
Normal file
15
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "api-schema-validator",
|
||||
"description": "Validate API schemas with JSON Schema, Joi, Yup, or Zod",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "Jeremy Longshore",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
"skills": [
|
||||
"./skills"
|
||||
],
|
||||
"commands": [
|
||||
"./commands"
|
||||
]
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# api-schema-validator
|
||||
|
||||
Validate API schemas with JSON Schema, Joi, Yup, or Zod
|
||||
598
commands/validate-schemas.md
Normal file
598
commands/validate-schemas.md
Normal file
@@ -0,0 +1,598 @@
|
||||
---
|
||||
description: Validate API schemas
|
||||
shortcut: schema
|
||||
---
|
||||
|
||||
# Validate API Schemas
|
||||
|
||||
Implement comprehensive schema validation using modern validation libraries like JSON Schema, Joi, Yup, or Zod to ensure type safety, data integrity, and contract compliance across your API.
|
||||
|
||||
## When to Use This Command
|
||||
|
||||
Use `/validate-schemas` when you need to:
|
||||
- Enforce strict data types and formats in API requests and responses
|
||||
- Create reusable validation schemas across multiple endpoints
|
||||
- Generate TypeScript types from validation schemas
|
||||
- Implement complex conditional validation logic
|
||||
- Provide detailed validation error messages to clients
|
||||
- Ensure data consistency before database operations
|
||||
|
||||
DON'T use this when:
|
||||
- Working with unstructured or highly dynamic data (use runtime checks instead)
|
||||
- Building quick prototypes without formal contracts (premature optimization)
|
||||
- Validation logic is trivial (simple type checks may suffice)
|
||||
|
||||
## Design Decisions
|
||||
|
||||
This command implements **Zod** as the primary approach because:
|
||||
- TypeScript-first with automatic type inference
|
||||
- Composable schemas with chaining syntax
|
||||
- Zero runtime dependencies
|
||||
- Excellent error messages out of the box
|
||||
- Schema transformation and parsing capabilities
|
||||
- Works seamlessly with modern frameworks
|
||||
|
||||
**Alternative considered: Joi**
|
||||
- More mature with extensive ecosystem
|
||||
- Better for JavaScript-only projects
|
||||
- More verbose API
|
||||
- Recommended for legacy Node.js applications
|
||||
|
||||
**Alternative considered: JSON Schema**
|
||||
- Language-agnostic standard
|
||||
- Better for cross-platform validation
|
||||
- More complex to write and maintain
|
||||
- Recommended when sharing schemas across different languages
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running this command:
|
||||
1. Choose validation library based on your tech stack
|
||||
2. Define validation requirements for each endpoint
|
||||
3. Plan error response format
|
||||
4. Consider performance impact for complex schemas
|
||||
5. Determine validation strategy (fail-fast vs. collect-all-errors)
|
||||
|
||||
## Implementation Process
|
||||
|
||||
### Step 1: Define Base Schemas
|
||||
Create reusable schema primitives for common data types and patterns.
|
||||
|
||||
### Step 2: Compose Endpoint Schemas
|
||||
Build complex schemas by composing base schemas with business rules.
|
||||
|
||||
### Step 3: Integrate with Middleware
|
||||
Set up validation middleware to automatically validate requests and responses.
|
||||
|
||||
### Step 4: Generate Types
|
||||
Generate TypeScript types from schemas for compile-time safety.
|
||||
|
||||
### Step 5: Implement Error Handling
|
||||
Create consistent error formatting and reporting mechanisms.
|
||||
|
||||
## Output Format
|
||||
|
||||
The command generates:
|
||||
- `schemas/` - Schema definitions organized by domain
|
||||
- `validators/` - Compiled validation functions
|
||||
- `types/` - Generated TypeScript types from schemas
|
||||
- `middleware/validation.ts` - Request/response validation middleware
|
||||
- `tests/schema.test.ts` - Schema validation test suites
|
||||
- `docs/validation-rules.md` - Documentation of all validation rules
|
||||
|
||||
## Code Examples
|
||||
|
||||
### Example 1: Zod Schema Validation (TypeScript)
|
||||
|
||||
```typescript
|
||||
// schemas/user.schema.ts
|
||||
import { z } from 'zod';
|
||||
|
||||
// Base schemas for reuse
|
||||
const emailSchema = z.string().email().toLowerCase();
|
||||
const passwordSchema = z.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Password must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Password must contain number')
|
||||
.regex(/[^A-Za-z0-9]/, 'Password must contain special character');
|
||||
|
||||
const phoneSchema = z.string().regex(
|
||||
/^\+?[1-9]\d{1,14}$/,
|
||||
'Invalid phone number format (E.164)'
|
||||
);
|
||||
|
||||
const addressSchema = z.object({
|
||||
street: z.string().min(1).max(100),
|
||||
city: z.string().min(1).max(50),
|
||||
state: z.string().length(2).toUpperCase(),
|
||||
zipCode: z.string().regex(/^\d{5}(-\d{4})?$/),
|
||||
country: z.string().length(2).toUpperCase().default('US')
|
||||
});
|
||||
|
||||
// User schemas
|
||||
export const createUserSchema = z.object({
|
||||
body: z.object({
|
||||
email: emailSchema,
|
||||
password: passwordSchema,
|
||||
firstName: z.string().min(1).max(50),
|
||||
lastName: z.string().min(1).max(50),
|
||||
dateOfBirth: z.string().datetime().refine(
|
||||
(date) => {
|
||||
const age = new Date().getFullYear() - new Date(date).getFullYear();
|
||||
return age >= 18;
|
||||
},
|
||||
{ message: 'Must be at least 18 years old' }
|
||||
),
|
||||
phone: phoneSchema.optional(),
|
||||
address: addressSchema.optional(),
|
||||
preferences: z.object({
|
||||
newsletter: z.boolean().default(false),
|
||||
notifications: z.enum(['email', 'sms', 'push', 'none']).default('email'),
|
||||
theme: z.enum(['light', 'dark', 'auto']).default('auto')
|
||||
}).default({}),
|
||||
metadata: z.record(z.string(), z.any()).optional()
|
||||
})
|
||||
});
|
||||
|
||||
export const updateUserSchema = z.object({
|
||||
params: z.object({
|
||||
id: z.string().uuid()
|
||||
}),
|
||||
body: createUserSchema.shape.body.partial().extend({
|
||||
// Can't update email without verification
|
||||
email: z.never().optional()
|
||||
})
|
||||
});
|
||||
|
||||
export const getUserSchema = z.object({
|
||||
params: z.object({
|
||||
id: z.string().uuid()
|
||||
})
|
||||
});
|
||||
|
||||
export const listUsersSchema = z.object({
|
||||
query: z.object({
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().min(1).max(100).default(20),
|
||||
sort: z.enum(['createdAt', 'email', 'name']).default('createdAt'),
|
||||
order: z.enum(['asc', 'desc']).default('desc'),
|
||||
filter: z.object({
|
||||
email: z.string().optional(),
|
||||
status: z.enum(['active', 'inactive', 'suspended']).optional(),
|
||||
createdAfter: z.string().datetime().optional(),
|
||||
createdBefore: z.string().datetime().optional()
|
||||
}).optional()
|
||||
})
|
||||
});
|
||||
|
||||
// Type inference
|
||||
export type CreateUserInput = z.infer<typeof createUserSchema>;
|
||||
export type UpdateUserInput = z.infer<typeof updateUserSchema>;
|
||||
export type GetUserInput = z.infer<typeof getUserSchema>;
|
||||
export type ListUsersInput = z.infer<typeof listUsersSchema>;
|
||||
|
||||
// Validation middleware
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
export function validate(schema: z.ZodSchema) {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const validated = await schema.parseAsync({
|
||||
body: req.body,
|
||||
query: req.query,
|
||||
params: req.params
|
||||
});
|
||||
|
||||
// Replace request properties with validated/transformed data
|
||||
req.body = validated.body || req.body;
|
||||
req.query = validated.query || req.query;
|
||||
req.params = validated.params || req.params;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return res.status(400).json({
|
||||
error: 'Validation failed',
|
||||
details: error.errors.map(err => ({
|
||||
path: err.path.join('.'),
|
||||
message: err.message,
|
||||
code: err.code
|
||||
}))
|
||||
});
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Usage in routes
|
||||
import express from 'express';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/users',
|
||||
validate(createUserSchema),
|
||||
async (req, res) => {
|
||||
// req.body is now typed and validated
|
||||
const user = await createUser(req.body);
|
||||
res.json(user);
|
||||
}
|
||||
);
|
||||
|
||||
router.put('/users/:id',
|
||||
validate(updateUserSchema),
|
||||
async (req, res) => {
|
||||
const user = await updateUser(req.params.id, req.body);
|
||||
res.json(user);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Example 2: Joi Schema Validation (JavaScript)
|
||||
|
||||
```javascript
|
||||
// schemas/product.schema.js
|
||||
const Joi = require('joi');
|
||||
|
||||
// Custom validators
|
||||
const customValidators = {
|
||||
sku: Joi.string().pattern(/^[A-Z]{3}-\d{6}$/),
|
||||
price: Joi.number().precision(2).positive().max(999999.99),
|
||||
url: Joi.string().uri({ scheme: ['http', 'https'] })
|
||||
};
|
||||
|
||||
// Product schemas
|
||||
const productSchema = {
|
||||
create: Joi.object({
|
||||
sku: customValidators.sku.required(),
|
||||
name: Joi.string().min(3).max(200).required(),
|
||||
description: Joi.string().max(2000).required(),
|
||||
price: customValidators.price.required(),
|
||||
compareAtPrice: customValidators.price
|
||||
.greater(Joi.ref('price'))
|
||||
.optional(),
|
||||
category: Joi.string().valid(
|
||||
'electronics',
|
||||
'clothing',
|
||||
'food',
|
||||
'books',
|
||||
'other'
|
||||
).required(),
|
||||
tags: Joi.array()
|
||||
.items(Joi.string().min(2).max(20))
|
||||
.max(10)
|
||||
.unique(),
|
||||
inventory: Joi.object({
|
||||
quantity: Joi.number().integer().min(0).required(),
|
||||
trackInventory: Joi.boolean().default(true),
|
||||
allowBackorder: Joi.boolean().default(false),
|
||||
lowStockThreshold: Joi.number().integer().min(0).default(10)
|
||||
}).required(),
|
||||
images: Joi.array()
|
||||
.items(Joi.object({
|
||||
url: customValidators.url.required(),
|
||||
alt: Joi.string().max(200),
|
||||
isPrimary: Joi.boolean().default(false)
|
||||
}))
|
||||
.min(1)
|
||||
.max(10)
|
||||
.unique('url')
|
||||
.required(),
|
||||
shipping: Joi.object({
|
||||
weight: Joi.number().positive().required(),
|
||||
dimensions: Joi.object({
|
||||
length: Joi.number().positive().required(),
|
||||
width: Joi.number().positive().required(),
|
||||
height: Joi.number().positive().required()
|
||||
}).required(),
|
||||
requiresShipping: Joi.boolean().default(true),
|
||||
shippingClass: Joi.string().valid('standard', 'fragile', 'oversized')
|
||||
}).when('category', {
|
||||
is: 'electronics',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional()
|
||||
}),
|
||||
variants: Joi.array()
|
||||
.items(Joi.object({
|
||||
name: Joi.string().required(),
|
||||
options: Joi.array()
|
||||
.items(Joi.string())
|
||||
.min(1)
|
||||
.required()
|
||||
}))
|
||||
.optional(),
|
||||
metadata: Joi.object().pattern(
|
||||
Joi.string(),
|
||||
Joi.alternatives().try(
|
||||
Joi.string(),
|
||||
Joi.number(),
|
||||
Joi.boolean()
|
||||
)
|
||||
).optional()
|
||||
}).custom((value, helpers) => {
|
||||
// Custom validation: ensure at least one primary image
|
||||
const primaryImages = value.images.filter(img => img.isPrimary);
|
||||
if (primaryImages.length !== 1) {
|
||||
return helpers.error('any.invalid', {
|
||||
message: 'Exactly one image must be marked as primary'
|
||||
});
|
||||
}
|
||||
return value;
|
||||
}),
|
||||
|
||||
update: Joi.object({
|
||||
name: Joi.string().min(3).max(200),
|
||||
description: Joi.string().max(2000),
|
||||
price: customValidators.price,
|
||||
// ... partial schema
|
||||
}).min(1), // At least one field required
|
||||
|
||||
list: Joi.object({
|
||||
page: Joi.number().integer().positive().default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
category: Joi.string(),
|
||||
minPrice: Joi.number().positive(),
|
||||
maxPrice: Joi.number().positive().greater(Joi.ref('minPrice')),
|
||||
search: Joi.string().max(100),
|
||||
inStock: Joi.boolean()
|
||||
})
|
||||
};
|
||||
|
||||
// Validation middleware
|
||||
function validateRequest(schemaName) {
|
||||
return async (req, res, next) => {
|
||||
const schema = productSchema[schemaName];
|
||||
if (!schema) {
|
||||
return next(new Error(`Schema ${schemaName} not found`));
|
||||
}
|
||||
|
||||
const dataToValidate = {
|
||||
...req.body,
|
||||
...req.query,
|
||||
...req.params
|
||||
};
|
||||
|
||||
try {
|
||||
const validated = await schema.validateAsync(dataToValidate, {
|
||||
abortEarly: false, // Return all errors
|
||||
stripUnknown: true, // Remove unknown keys
|
||||
convert: true // Type coercion
|
||||
});
|
||||
|
||||
// Merge validated data back
|
||||
Object.keys(validated).forEach(key => {
|
||||
if (req.body.hasOwnProperty(key)) req.body[key] = validated[key];
|
||||
if (req.query.hasOwnProperty(key)) req.query[key] = validated[key];
|
||||
if (req.params.hasOwnProperty(key)) req.params[key] = validated[key];
|
||||
});
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error.isJoi) {
|
||||
return res.status(400).json({
|
||||
error: 'Validation failed',
|
||||
details: error.details.map(detail => ({
|
||||
field: detail.path.join('.'),
|
||||
message: detail.message,
|
||||
type: detail.type
|
||||
}))
|
||||
});
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
productSchema,
|
||||
validateRequest
|
||||
};
|
||||
```
|
||||
|
||||
### Example 3: Pydantic Schema Validation (Python)
|
||||
|
||||
```python
|
||||
# schemas/order_schema.py
|
||||
from pydantic import BaseModel, Field, validator, root_validator
|
||||
from typing import List, Optional, Dict, Any
|
||||
from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
import re
|
||||
|
||||
class OrderStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
PROCESSING = "processing"
|
||||
SHIPPED = "shipped"
|
||||
DELIVERED = "delivered"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
class PaymentMethod(str, Enum):
|
||||
CREDIT_CARD = "credit_card"
|
||||
DEBIT_CARD = "debit_card"
|
||||
PAYPAL = "paypal"
|
||||
STRIPE = "stripe"
|
||||
BANK_TRANSFER = "bank_transfer"
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str = Field(..., min_length=1, max_length=200)
|
||||
city: str = Field(..., min_length=1, max_length=100)
|
||||
state: str = Field(..., regex="^[A-Z]{2}$")
|
||||
zip_code: str = Field(..., regex=r"^\d{5}(-\d{4})?$")
|
||||
country: str = Field(default="US", regex="^[A-Z]{2}$")
|
||||
|
||||
@validator('state', 'country')
|
||||
def uppercase_codes(cls, v):
|
||||
return v.upper()
|
||||
|
||||
class OrderItem(BaseModel):
|
||||
product_id: str = Field(..., regex="^[A-Z]{3}-\d{6}$")
|
||||
quantity: int = Field(..., gt=0, le=100)
|
||||
unit_price: Decimal = Field(..., decimal_places=2, ge=0)
|
||||
discount: Optional[Decimal] = Field(None, decimal_places=2, ge=0, le=1)
|
||||
|
||||
@validator('discount')
|
||||
def validate_discount(cls, v, values):
|
||||
if v and v >= 1:
|
||||
raise ValueError('Discount must be less than 100%')
|
||||
return v
|
||||
|
||||
@property
|
||||
def subtotal(self) -> Decimal:
|
||||
discount_amount = self.discount or Decimal('0')
|
||||
return self.quantity * self.unit_price * (1 - discount_amount)
|
||||
|
||||
class CreateOrderSchema(BaseModel):
|
||||
customer_email: str = Field(..., regex=r"^[\w\.-]+@[\w\.-]+\.\w+$")
|
||||
items: List[OrderItem] = Field(..., min_items=1, max_items=50)
|
||||
shipping_address: Address
|
||||
billing_address: Optional[Address] = None
|
||||
payment_method: PaymentMethod
|
||||
notes: Optional[str] = Field(None, max_length=500)
|
||||
coupon_code: Optional[str] = Field(None, regex="^[A-Z0-9]{4,12}$")
|
||||
|
||||
@validator('customer_email')
|
||||
def validate_email(cls, v):
|
||||
# Additional email validation
|
||||
if not re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", v.lower()):
|
||||
raise ValueError('Invalid email format')
|
||||
return v.lower()
|
||||
|
||||
@validator('billing_address', always=True)
|
||||
def set_billing_address(cls, v, values):
|
||||
# Use shipping address if billing not provided
|
||||
return v or values.get('shipping_address')
|
||||
|
||||
@root_validator
|
||||
def validate_order(cls, values):
|
||||
items = values.get('items', [])
|
||||
|
||||
# Check for duplicate products
|
||||
product_ids = [item.product_id for item in items]
|
||||
if len(product_ids) != len(set(product_ids)):
|
||||
raise ValueError('Duplicate products in order')
|
||||
|
||||
# Calculate total
|
||||
total = sum(item.subtotal for item in items)
|
||||
if total <= 0:
|
||||
raise ValueError('Order total must be positive')
|
||||
|
||||
# Validate coupon if provided
|
||||
coupon = values.get('coupon_code')
|
||||
if coupon and not cls.validate_coupon(coupon):
|
||||
raise ValueError('Invalid coupon code')
|
||||
|
||||
return values
|
||||
|
||||
@staticmethod
|
||||
def validate_coupon(code: str) -> bool:
|
||||
# Implement coupon validation logic
|
||||
valid_coupons = ['SAVE10', 'FREESHIP', 'WELCOME20']
|
||||
return code in valid_coupons
|
||||
|
||||
class UpdateOrderSchema(BaseModel):
|
||||
status: Optional[OrderStatus] = None
|
||||
shipping_address: Optional[Address] = None
|
||||
notes: Optional[str] = Field(None, max_length=500)
|
||||
tracking_number: Optional[str] = Field(None, regex=r"^[A-Z0-9]{10,30}$")
|
||||
|
||||
class Config:
|
||||
use_enum_values = True
|
||||
|
||||
# FastAPI integration
|
||||
from fastapi import FastAPI, HTTPException, Depends
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.post("/orders", response_model=Dict[str, Any])
|
||||
async def create_order(order: CreateOrderSchema):
|
||||
# Validation happens automatically
|
||||
order_dict = jsonable_encoder(order)
|
||||
# Process order
|
||||
return {"id": "ORD-123456", **order_dict}
|
||||
|
||||
@app.patch("/orders/{order_id}")
|
||||
async def update_order(
|
||||
order_id: str,
|
||||
updates: UpdateOrderSchema
|
||||
):
|
||||
# Only provided fields will be updated
|
||||
update_dict = updates.dict(exclude_unset=True)
|
||||
# Update order
|
||||
return {"id": order_id, **update_dict}
|
||||
|
||||
# Custom validation endpoint
|
||||
@app.post("/validate/order")
|
||||
async def validate_order(order: CreateOrderSchema):
|
||||
# Just validate without processing
|
||||
return {"valid": True, "data": order.dict()}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| "Invalid type" | Wrong data type provided | Check schema definition and input data |
|
||||
| "Required field missing" | Mandatory field not provided | Ensure all required fields are present |
|
||||
| "Validation failed" | Business rule violation | Review custom validators and constraints |
|
||||
| "Schema not found" | Referenced schema doesn't exist | Verify schema imports and definitions |
|
||||
| "Circular dependency" | Schema references itself | Refactor to break circular references |
|
||||
|
||||
## Configuration Options
|
||||
|
||||
**Validation Strategies**
|
||||
- `fail-fast`: Stop at first error (faster)
|
||||
- `collect-all`: Gather all errors (better UX)
|
||||
- `partial`: Allow partial validation for updates
|
||||
|
||||
**Type Coercion**
|
||||
- `strict`: No type conversion
|
||||
- `loose`: Attempt type conversion
|
||||
- `smart`: Context-aware conversion
|
||||
|
||||
## Best Practices
|
||||
|
||||
DO:
|
||||
- Create reusable base schemas for common patterns
|
||||
- Use descriptive error messages for custom validators
|
||||
- Generate TypeScript types from schemas
|
||||
- Version schemas with your API
|
||||
- Document all validation rules
|
||||
- Test edge cases and boundary conditions
|
||||
|
||||
DON'T:
|
||||
- Duplicate validation logic across layers
|
||||
- Use overly complex nested schemas
|
||||
- Ignore performance impact of complex validations
|
||||
- Mix validation with business logic
|
||||
- Trust client-side validation alone
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- Compile schemas once at startup
|
||||
- Cache validated results for identical inputs
|
||||
- Use async validation for external checks
|
||||
- Limit regex complexity to prevent ReDoS attacks
|
||||
- Consider schema complexity for large payloads
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Sanitize error messages to prevent information leakage
|
||||
- Implement rate limiting on validation endpoints
|
||||
- Use strict type checking to prevent injection attacks
|
||||
- Validate file uploads separately with size limits
|
||||
- Never expose internal schema structure to clients
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/api-response-validator` - Validate API responses
|
||||
- `/api-contract-generator` - Generate schemas from code
|
||||
- `/api-testing-framework` - Test with schema validation
|
||||
- `/api-documentation-generator` - Document schemas
|
||||
|
||||
## Version History
|
||||
|
||||
- v1.0.0 (2024-10): Initial implementation with Zod, Joi, and Pydantic support
|
||||
- Planned v1.1.0: Add support for Yup and JSON Schema generation
|
||||
85
plugin.lock.json
Normal file
85
plugin.lock.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||
"pluginId": "gh:jeremylongshore/claude-code-plugins-plus:plugins/api-development/api-schema-validator",
|
||||
"normalized": {
|
||||
"repo": null,
|
||||
"ref": "refs/tags/v20251128.0",
|
||||
"commit": "6fe99a526667bc7dd391d56742a23288b3bd1d53",
|
||||
"treeHash": "b459f932e847d68e0e05b35c5895225bc94d064291e4b9f5270a8ef65918b307",
|
||||
"generatedAt": "2025-11-28T10:18:08.393637Z",
|
||||
"toolVersion": "publish_plugins.py@0.2.0"
|
||||
},
|
||||
"origin": {
|
||||
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||
"branch": "master",
|
||||
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||
},
|
||||
"manifest": {
|
||||
"name": "api-schema-validator",
|
||||
"description": "Validate API schemas with JSON Schema, Joi, Yup, or Zod",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"content": {
|
||||
"files": [
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "964deefdc89c0c6afb1af808195fbe152019a4c1bfffc8f3afc0d73245dcfc44"
|
||||
},
|
||||
{
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"sha256": "ee709d51ea913c41f0b22736ce467df18dc635d400156c703d512ee4c248c5f1"
|
||||
},
|
||||
{
|
||||
"path": "commands/validate-schemas.md",
|
||||
"sha256": "4b90ff709dc69abb52eacb253f61603dfe9d00ab397507a8729f789fde9293be"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/references/examples.md",
|
||||
"sha256": "922bbc3c4ebf38b76f515b5c1998ebde6bf902233e00e2c5a0e9176f975a7572"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/references/best-practices.md",
|
||||
"sha256": "c8f32b3566252f50daacd346d7045a1060c718ef5cfb07c55a0f2dec5f1fb39e"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/references/README.md",
|
||||
"sha256": "792835f31b3849b180d74f135ac6fe79cc3ed93b6c826b2ea62d6d6703724dab"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/scripts/helper-template.sh",
|
||||
"sha256": "0881d5660a8a7045550d09ae0acc15642c24b70de6f08808120f47f86ccdf077"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/scripts/validation.sh",
|
||||
"sha256": "92551a29a7f512d2036e4f1fb46c2a3dc6bff0f7dde4a9f699533e446db48502"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/scripts/README.md",
|
||||
"sha256": "f2f6a48cd8eb28031af2c6040a6bf96d1f8fa3a9780e8f4bf884ea16bfd3b89d"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/assets/test-data.json",
|
||||
"sha256": "ac17dca3d6e253a5f39f2a2f1b388e5146043756b05d9ce7ac53a0042eee139d"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/assets/README.md",
|
||||
"sha256": "784881a613b5da6273956467a368958c1c73d156a8122cbe93d85d62a6c83978"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/assets/skill-schema.json",
|
||||
"sha256": "f5639ba823a24c9ac4fb21444c0717b7aefde1a4993682897f5bf544f863c2cd"
|
||||
},
|
||||
{
|
||||
"path": "skills/skill-adapter/assets/config-template.json",
|
||||
"sha256": "0c2ba33d2d3c5ccb266c0848fc43caa68a2aa6a80ff315d4b378352711f83e1c"
|
||||
}
|
||||
],
|
||||
"dirSha256": "b459f932e847d68e0e05b35c5895225bc94d064291e4b9f5270a8ef65918b307"
|
||||
},
|
||||
"security": {
|
||||
"scannedAt": null,
|
||||
"scannerVersion": null,
|
||||
"flags": []
|
||||
}
|
||||
}
|
||||
7
skills/skill-adapter/assets/README.md
Normal file
7
skills/skill-adapter/assets/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Assets
|
||||
|
||||
Bundled resources for api-schema-validator skill
|
||||
|
||||
- [ ] example_schemas/: A directory containing example schemas in various formats (JSON Schema, Joi, Yup, Zod).
|
||||
- [ ] test_data/: A directory containing example data files to validate against the schemas.
|
||||
- [ ] schema_templates/: Templates for creating new schemas in different formats.
|
||||
32
skills/skill-adapter/assets/config-template.json
Normal file
32
skills/skill-adapter/assets/config-template.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"skill": {
|
||||
"name": "skill-name",
|
||||
"version": "1.0.0",
|
||||
"enabled": true,
|
||||
"settings": {
|
||||
"verbose": false,
|
||||
"autoActivate": true,
|
||||
"toolRestrictions": true
|
||||
}
|
||||
},
|
||||
"triggers": {
|
||||
"keywords": [
|
||||
"example-trigger-1",
|
||||
"example-trigger-2"
|
||||
],
|
||||
"patterns": []
|
||||
},
|
||||
"tools": {
|
||||
"allowed": [
|
||||
"Read",
|
||||
"Grep",
|
||||
"Bash"
|
||||
],
|
||||
"restricted": []
|
||||
},
|
||||
"metadata": {
|
||||
"author": "Plugin Author",
|
||||
"category": "general",
|
||||
"tags": []
|
||||
}
|
||||
}
|
||||
28
skills/skill-adapter/assets/skill-schema.json
Normal file
28
skills/skill-adapter/assets/skill-schema.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Claude Skill Configuration",
|
||||
"type": "object",
|
||||
"required": ["name", "description"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9-]+$",
|
||||
"maxLength": 64,
|
||||
"description": "Skill identifier (lowercase, hyphens only)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"maxLength": 1024,
|
||||
"description": "What the skill does and when to use it"
|
||||
},
|
||||
"allowed-tools": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of allowed tools"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
||||
"description": "Semantic version (x.y.z)"
|
||||
}
|
||||
}
|
||||
}
|
||||
27
skills/skill-adapter/assets/test-data.json
Normal file
27
skills/skill-adapter/assets/test-data.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"testCases": [
|
||||
{
|
||||
"name": "Basic activation test",
|
||||
"input": "trigger phrase example",
|
||||
"expected": {
|
||||
"activated": true,
|
||||
"toolsUsed": ["Read", "Grep"],
|
||||
"success": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Complex workflow test",
|
||||
"input": "multi-step trigger example",
|
||||
"expected": {
|
||||
"activated": true,
|
||||
"steps": 3,
|
||||
"toolsUsed": ["Read", "Write", "Bash"],
|
||||
"success": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixtures": {
|
||||
"sampleInput": "example data",
|
||||
"expectedOutput": "processed result"
|
||||
}
|
||||
}
|
||||
9
skills/skill-adapter/references/README.md
Normal file
9
skills/skill-adapter/references/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# References
|
||||
|
||||
Bundled resources for api-schema-validator skill
|
||||
|
||||
- [ ] json_schema_spec.md: A markdown file containing a summary of the JSON Schema specification.
|
||||
- [ ] joi_documentation.md: A markdown file containing key excerpts from the Joi documentation.
|
||||
- [ ] yup_documentation.md: A markdown file containing key excerpts from the Yup documentation.
|
||||
- [ ] zod_documentation.md: A markdown file containing key excerpts from the Zod documentation.
|
||||
- [ ] schema_best_practices.md: A guide to writing effective and maintainable schemas.
|
||||
69
skills/skill-adapter/references/best-practices.md
Normal file
69
skills/skill-adapter/references/best-practices.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Skill Best Practices
|
||||
|
||||
Guidelines for optimal skill usage and development.
|
||||
|
||||
## For Users
|
||||
|
||||
### Activation Best Practices
|
||||
|
||||
1. **Use Clear Trigger Phrases**
|
||||
- Match phrases from skill description
|
||||
- Be specific about intent
|
||||
- Provide necessary context
|
||||
|
||||
2. **Provide Sufficient Context**
|
||||
- Include relevant file paths
|
||||
- Specify scope of analysis
|
||||
- Mention any constraints
|
||||
|
||||
3. **Understand Tool Permissions**
|
||||
- Check allowed-tools in frontmatter
|
||||
- Know what the skill can/cannot do
|
||||
- Request appropriate actions
|
||||
|
||||
### Workflow Optimization
|
||||
|
||||
- Start with simple requests
|
||||
- Build up to complex workflows
|
||||
- Verify each step before proceeding
|
||||
- Use skill consistently for related tasks
|
||||
|
||||
## For Developers
|
||||
|
||||
### Skill Development Guidelines
|
||||
|
||||
1. **Clear Descriptions**
|
||||
- Include explicit trigger phrases
|
||||
- Document all capabilities
|
||||
- Specify limitations
|
||||
|
||||
2. **Proper Tool Permissions**
|
||||
- Use minimal necessary tools
|
||||
- Document security implications
|
||||
- Test with restricted tools
|
||||
|
||||
3. **Comprehensive Documentation**
|
||||
- Provide usage examples
|
||||
- Document common pitfalls
|
||||
- Include troubleshooting guide
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Keep version updated
|
||||
- Test after tool updates
|
||||
- Monitor user feedback
|
||||
- Iterate on descriptions
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- Scope skills to specific domains
|
||||
- Avoid overlapping trigger phrases
|
||||
- Keep descriptions under 1024 chars
|
||||
- Test activation reliability
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Never include secrets in skill files
|
||||
- Validate all inputs
|
||||
- Use read-only tools when possible
|
||||
- Document security requirements
|
||||
70
skills/skill-adapter/references/examples.md
Normal file
70
skills/skill-adapter/references/examples.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Skill Usage Examples
|
||||
|
||||
This document provides practical examples of how to use this skill effectively.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Example 1: Simple Activation
|
||||
|
||||
**User Request:**
|
||||
```
|
||||
[Describe trigger phrase here]
|
||||
```
|
||||
|
||||
**Skill Response:**
|
||||
1. Analyzes the request
|
||||
2. Performs the required action
|
||||
3. Returns results
|
||||
|
||||
### Example 2: Complex Workflow
|
||||
|
||||
**User Request:**
|
||||
```
|
||||
[Describe complex scenario]
|
||||
```
|
||||
|
||||
**Workflow:**
|
||||
1. Step 1: Initial analysis
|
||||
2. Step 2: Data processing
|
||||
3. Step 3: Result generation
|
||||
4. Step 4: Validation
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Pattern 1: Chaining Operations
|
||||
|
||||
Combine this skill with other tools:
|
||||
```
|
||||
Step 1: Use this skill for [purpose]
|
||||
Step 2: Chain with [other tool]
|
||||
Step 3: Finalize with [action]
|
||||
```
|
||||
|
||||
### Pattern 2: Error Handling
|
||||
|
||||
If issues occur:
|
||||
- Check trigger phrase matches
|
||||
- Verify context is available
|
||||
- Review allowed-tools permissions
|
||||
|
||||
## Tips & Best Practices
|
||||
|
||||
- ✅ Be specific with trigger phrases
|
||||
- ✅ Provide necessary context
|
||||
- ✅ Check tool permissions match needs
|
||||
- ❌ Avoid vague requests
|
||||
- ❌ Don't mix unrelated tasks
|
||||
|
||||
## Common Issues
|
||||
|
||||
**Issue:** Skill doesn't activate
|
||||
**Solution:** Use exact trigger phrases from description
|
||||
|
||||
**Issue:** Unexpected results
|
||||
**Solution:** Check input format and context
|
||||
|
||||
## See Also
|
||||
|
||||
- Main SKILL.md for full documentation
|
||||
- scripts/ for automation helpers
|
||||
- assets/ for configuration examples
|
||||
7
skills/skill-adapter/scripts/README.md
Normal file
7
skills/skill-adapter/scripts/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Scripts
|
||||
|
||||
Bundled resources for api-schema-validator skill
|
||||
|
||||
- [ ] validate_schema.py: Script to validate a given schema against a data file or API endpoint.
|
||||
- [ ] generate_schema.py: Script to generate a basic schema from a data file or API endpoint response.
|
||||
- [ ] convert_schema.py: Script to convert between different schema formats (e.g., JSON Schema to Yup).
|
||||
42
skills/skill-adapter/scripts/helper-template.sh
Executable file
42
skills/skill-adapter/scripts/helper-template.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# Helper script template for skill automation
|
||||
# Customize this for your skill's specific needs
|
||||
|
||||
set -e
|
||||
|
||||
function show_usage() {
|
||||
echo "Usage: $0 [options]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -h, --help Show this help message"
|
||||
echo " -v, --verbose Enable verbose output"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
VERBOSE=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Your skill logic here
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
echo "Running skill automation..."
|
||||
fi
|
||||
|
||||
echo "✅ Complete"
|
||||
32
skills/skill-adapter/scripts/validation.sh
Executable file
32
skills/skill-adapter/scripts/validation.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
# Skill validation helper
|
||||
# Validates skill activation and functionality
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔍 Validating skill..."
|
||||
|
||||
# Check if SKILL.md exists
|
||||
if [ ! -f "../SKILL.md" ]; then
|
||||
echo "❌ Error: SKILL.md not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate frontmatter
|
||||
if ! grep -q "^---$" "../SKILL.md"; then
|
||||
echo "❌ Error: No frontmatter found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check required fields
|
||||
if ! grep -q "^name:" "../SKILL.md"; then
|
||||
echo "❌ Error: Missing 'name' field"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "^description:" "../SKILL.md"; then
|
||||
echo "❌ Error: Missing 'description' field"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Skill validation passed"
|
||||
Reference in New Issue
Block a user