@contract-kit/devtools
v0.1.2
Published
Development-time devtools for Contract Kit - mini Telescope for the server runtime
Maintainers
Readme
@contract-kit/devtools
Development-time devtools for the Contract Kit framework. Provides a mini "Telescope" for debugging and observability during development.
Features
- 📊 In-memory event buffer - Stores recent framework events (requests, errors, use cases, jobs, etc.)
- 🔍 Event filtering - Filter by type, requestId for correlation
- 🚀 HTTP handlers - Expose devtools data via simple JSON endpoints
- 🛡️ Production-safe - Automatically disabled in production environments
- 📦 Minimal dependencies -
zodfor config parsing plus the@contract-kit/portspeer dependency
Installation
bun add @contract-kit/devtoolsTypeScript Requirements
This package requires TypeScript 5.0 or higher for proper type inference.
Usage
1. Register the provider
Wire the devtoolsProvider into your Contract Kit server:
import { devtoolsProvider } from "@contract-kit/devtools";
import { createServer } from "@contract-kit/server";
const server = await createServer({
ports,
providers: [
devtoolsProvider,
// ... other providers
],
});The provider will:
- Attach a
DevtoolsPorttoctx.ports.devtools - Be enabled by default in non-production environments
- Provide a no-op implementation in production for safety
2. Log events in your application
The Contract Kit framework and your application code can log events:
Request events
ctx.ports.devtools?.log({
id: crypto.randomUUID(),
type: "request",
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
method: "GET",
path: "/api/users",
contractName: "users.list",
status: 200,
durationMs: 45,
});Error events
ctx.ports.devtools?.log({
id: crypto.randomUUID(),
type: "error",
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
message: err.message,
stack: err.stack,
contractName: "users.create",
});Use case events
// Start
ctx.ports.devtools?.log({
id: crypto.randomUUID(),
type: "usecase",
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
name: "createUser",
phase: "start",
});
// End
ctx.ports.devtools?.log({
id: crypto.randomUUID(),
type: "usecase",
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
name: "createUser",
phase: "end",
durationMs: 123,
});Event bus events
ctx.ports.devtools?.log({
id: crypto.randomUUID(),
type: "eventBus",
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
eventName: "user.registered",
});Job events
ctx.ports.devtools?.log({
id: crypto.randomUUID(),
type: "job",
timestamp: new Date().toISOString(),
jobName: "sendWelcomeEmail",
status: "scheduled",
});Provider events
ctx.ports.devtools?.log({
id: crypto.randomUUID(),
type: "provider",
timestamp: new Date().toISOString(),
providerName: "redis",
action: "register",
});3. Set up HTTP endpoints
Expose devtools data via HTTP handlers (example for Next.js App Router):
List events endpoint
// app/api/devtools/events/route.ts
import { handleDevtoolsEventsRequest } from "@contract-kit/devtools";
import { getAppContext } from "@/lib/server"; // Your app-specific helper
export async function GET(req: Request) {
const ctx = await getAppContext();
return handleDevtoolsEventsRequest(req, ctx.ports.devtools);
}Query parameters:
type- Filter by event type (request, error, usecase, eventBus, job, provider)requestId- Filter by correlation IDlimit- Maximum events to return (default: 200)
Example requests:
# Get all events
GET /api/devtools/events
# Get only error events
GET /api/devtools/events?type=error
# Get events for a specific request
GET /api/devtools/events?requestId=req-123
# Limit results
GET /api/devtools/events?limit=50Clear events endpoint
// app/api/devtools/clear/route.ts
import { handleDevtoolsClearRequest } from "@contract-kit/devtools";
import { getAppContext } from "@/lib/server";
export async function POST(req: Request) {
const ctx = await getAppContext();
return handleDevtoolsClearRequest(req, ctx.ports.devtools);
}Example request:
POST /api/devtools/clearConfiguration
Environment Variables
Control whether devtools is enabled:
# Explicitly enable (overrides NODE_ENV check)
DEVTOOLS_ENABLED=true
# Explicitly disable
DEVTOOLS_ENABLED=falseBy default, devtools is:
- ✅ Enabled when
NODE_ENV !== "production" - ❌ Disabled when
NODE_ENV === "production"
Event Buffer Size
The in-memory buffer stores a maximum of 500 events. When this limit is reached, the oldest events are automatically removed.
The getEvents() method applies a default limit of 200 events unless specified otherwise.
Event Types
All events share common fields:
interface BaseDevtoolsEvent {
id: string; // Unique identifier (UUID recommended)
timestamp: string; // ISO 8601 timestamp
requestId?: string; // Correlation ID for tracing
}Request Events
{
type: "request",
method: string, // HTTP method
path: string, // Request path
contractName?: string, // Contract that handled this request
status?: number, // HTTP status code
durationMs?: number, // Request duration
summary?: string, // Short summary
}Error Events
{
type: "error",
message: string, // Error message
stack?: string, // Stack trace
contractName?: string, // Contract where error occurred
useCaseName?: string, // Use case where error occurred
}Use Case Events
{
type: "usecase",
name: string, // Use case name
phase: "start" | "end",
durationMs?: number, // Duration (for "end" phase)
}Event Bus Events
{
type: "eventBus",
eventName: string, // Name of the domain event published
}Job Events
{
type: "job",
jobName: string, // Job name
status: "scheduled" | "started" | "completed" | "failed",
}Provider Events
{
type: "provider",
providerName: string, // Provider name
action: "register" | "onAppStart" | "onAppStop",
}API Reference
DevtoolsPort
The port interface that gets attached to ctx.ports.devtools:
interface DevtoolsPort {
log(event: DevtoolsEvent): void;
getEvents(filter?: DevtoolsFilter): DevtoolsEvent[];
clear(): void;
}DevtoolsFilter
interface DevtoolsFilter {
type?: DevtoolsEvent["type"];
requestId?: string;
limit?: number;
}Contract Kit Server Integration
The Contract Kit server runtime should automatically log events at key points:
- Request handling - Log request start/end with timing
- Error handling - Log unhandled errors
- Use case execution - Log use case start/end
- Event bus - Log domain event publishing
- Job scheduling - Log job lifecycle events
- Provider registration - Log provider lifecycle
Example integration in the server runtime:
// Inside createServer request handler
async function handleRequest(req: Request) {
const requestId = crypto.randomUUID();
const startTime = Date.now();
try {
// Log request start
ports.devtools?.log({
id: crypto.randomUUID(),
type: "request",
timestamp: new Date().toISOString(),
requestId,
method: req.method,
path: new URL(req.url).pathname,
});
// Handle request...
const response = await handleRoute(req);
// Log request end
ports.devtools?.log({
id: crypto.randomUUID(),
type: "request",
timestamp: new Date().toISOString(),
requestId,
method: req.method,
path: new URL(req.url).pathname,
status: response.status,
durationMs: Date.now() - startTime,
});
return response;
} catch (err) {
// Log error
ports.devtools?.log({
id: crypto.randomUUID(),
type: "error",
timestamp: new Date().toISOString(),
requestId,
message: err.message,
stack: err.stack,
});
throw err;
}
}Future Enhancements
- 🎨 Simple React UI for visualizing events
- 📈 Performance metrics and charts
- 💾 Persistent storage option (SQLite, etc.)
- 🎯 More granular event filtering
- 📊 Aggregated statistics and insights
License
MIT
