Manifest System
Gati uses a sophisticated manifest system for fast, incremental route discovery and hot reloading. This system enables zero-configuration development with instant feedback.
How It Works
The manifest system operates in three layers:
- File Analysis - Extract route information from TypeScript files
- Individual Manifests - Create JSON manifests for each file
- Application Manifest - Aggregate all routes into a single manifest
plaintext
src/handlers/hello.ts
↓ (analyze)
.gati/manifests/hello.json
↓ (aggregate)
.gati/manifests/_app.json
↓ (load)
Running ApplicationIndividual File Manifests
Each handler file gets its own manifest in .gati/manifests/:
Example: src/handlers/users/[id].ts
typescript
export const METHOD = 'GET';
export const ROUTE = '/users/:id';
export const handler: Handler = (req, res) => {
res.json({ userId: req.params.id });
};Generated: .gati/manifests/users_[id].json
json
{
"filePath": "/project/src/handlers/users/[id].ts",
"type": "handler",
"data": {
"filePath": "/project/src/handlers/users/[id].ts",
"relativePath": "handlers/users/[id].ts",
"route": "/users/:id",
"method": "GET",
"exportName": "handler",
"exportType": "named"
},
"timestamp": 1699123456789
}Application Manifest
All individual manifests are aggregated into _app.json:
json
{
"handlers": [
{
"filePath": "/project/src/handlers/hello.ts",
"route": "/hello",
"method": "GET",
"exportName": "handler"
},
{
"filePath": "/project/src/handlers/users/[id].ts",
"route": "/users/:id",
"method": "GET",
"exportName": "handler"
}
],
"modules": [
{
"filePath": "/project/src/modules/database.ts",
"exportName": "database",
"methods": ["findById", "create", "update"]
}
],
"timestamp": 1699123456789
}File Watching Strategy
Gati uses parallel watching for optimal performance:
1. Source File Watcher
Monitors src/**/*.{ts,js} for changes:
typescript
// When src/handlers/users.ts changes:
1. Analyze file → Extract METHOD, ROUTE, handler
2. Update users.json manifest
3. Trigger manifest watcher2. Manifest Watcher
Monitors .gati/manifests/*.json for changes:
typescript
// When users.json changes:
1. Read all individual manifests
2. Aggregate into _app.json
3. Trigger application reloadThis parallel approach ensures:
- ✅ Fast Updates - Only changed files are reprocessed
- ✅ Incremental Builds - No full project analysis needed
- ✅ Reliable Sync - Manifests always reflect current state
Manifest Naming
Individual manifests use clean, readable names:
| File Path | Manifest Name |
|---|---|
src/handlers/hello.ts | hello.json |
src/handlers/users/[id].ts | users_[id].json |
src/handlers/posts/[id]/comments.ts | posts_[id]_comments.json |
src/modules/database.ts | database.json |
Route Analysis
The analyzer extracts route information using regex patterns:
METHOD Export
typescript
export const METHOD = 'POST';
// Extracted: method = 'POST'ROUTE Export (Optional)
typescript
export const ROUTE = '/custom/path';
// Extracted: route = '/custom/path'Handler Export
typescript
export const handler: Handler = (req, res) => { /* ... */ };
// Extracted: exportName = 'handler', exportType = 'named'Auto-Generated Routes
If no ROUTE export, route is generated from file path:
plaintext
handlers/users/[id].ts → /users/:id
handlers/posts/create.ts → /posts/create
handlers/hello.ts → /helloDevelopment Workflow
1. Initial Scan
When starting pnpm dev:
plaintext
🔍 Scanning src/ directory...
📄 Found 5 handlers, 2 modules
✅ Generated manifests in .gati/manifests/
🚀 Server ready at http://localhost:30002. File Changes
When you modify a file:
plaintext
📝 File changed: users/[id].ts
✅ Updated manifest: users_[id].json
🔄 Reloaded GET /users/:id3. New Files
When you add a file:
plaintext
📄 File added: posts/create.ts
✅ Created manifest: posts_create.json
🆕 Registered POST /posts/create4. Deleted Files
When you delete a file:
plaintext
🗑️ File removed: old-handler.ts
🗑️ Removed manifest: old-handler.json
❌ Unregistered GET /old-handlerPerformance Benefits
Traditional Approach
plaintext
File Change → Full Project Scan → Rebuild All Routes → Restart Server
Time: ~2-5 secondsGati Manifest System
plaintext
File Change → Update Single Manifest → Reload Changed Route
Time: ~50-200msBenchmarks
- Cold Start: 500ms for 100 handlers
- Hot Reload: 50ms per file change
- Memory Usage: ~1MB for 1000 handlers
Manifest Structure
Handler Manifest
typescript
interface HandlerManifest {
filePath: string; // Absolute file path
type: 'handler'; // Manifest type
data: {
filePath: string; // Same as above
relativePath: string; // Relative to src/
route: string; // API route (e.g., '/users/:id')
method: string; // HTTP method
exportName: string; // Handler function name
exportType: 'named'; // Export type
};
timestamp: number; // Last modified time
}Module Manifest
typescript
interface ModuleManifest {
filePath: string; // Absolute file path
type: 'module'; // Manifest type
data: {
filePath: string; // Same as above
exportName: string; // Module name
exportType: 'named'; // Export type
methods: string[]; // Available methods
};
timestamp: number; // Last modified time
}Error Handling
Invalid Syntax
plaintext
❌ Failed to process users.ts: SyntaxError
⚠️ Skipping invalid fileMissing Exports
plaintext
⚠️ No handler export found in users.ts
⚠️ Skipping file without handlerRoute Conflicts
plaintext
⚠️ Route conflict: GET /users
- handlers/users.ts
- handlers/users/index.ts
✅ Using handlers/users.ts (higher priority)Configuration
Disable File Watching
bash
pnpm dev --no-watchCustom Manifest Directory
typescript
// gati.config.ts
export default {
manifestDir: '.custom-manifests'
};Ignore Patterns
typescript
// gati.config.ts
export default {
ignore: [
'src/handlers/internal/**',
'src/handlers/*.test.ts'
]
};Debugging Manifests
View Individual Manifest
bash
cat .gati/manifests/users_[id].jsonView Application Manifest
bash
cat .gati/manifests/_app.json | jq '.handlers'Watch Manifest Changes
bash
# Terminal 1: Watch manifests
watch -n 1 'ls -la .gati/manifests/'
# Terminal 2: Make changes
echo "// comment" >> src/handlers/hello.tsTroubleshooting
Manifests Not Updating
- Check file permissions on
.gati/directory - Ensure TypeScript syntax is valid
- Verify exports are present
Routes Not Loading
- Check
_app.jsoncontains your route - Verify handler export name matches manifest
- Check for route conflicts in logs
Performance Issues
- Reduce file watching scope
- Use
.gitignorepatterns to exclude files - Check for circular dependencies
Advanced Usage
Custom Analyzer
typescript
// gati.config.ts
export default {
analyzer: {
// Custom route extraction logic
extractRoute: (content: string, filePath: string) => {
// Your custom logic
}
}
};Manifest Hooks
typescript
// gati.config.ts
export default {
hooks: {
onManifestUpdate: (manifest) => {
console.log('Routes updated:', manifest.handlers.length);
}
}
};Next Steps
- Hot Reloading - Development workflow
- File-Based Routing - Route conventions
- Development Server - Server features