Skip to content

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:

  1. File Analysis - Extract route information from TypeScript files
  2. Individual Manifests - Create JSON manifests for each file
  3. 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 Application

Individual 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 watcher

2. 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 reload

This 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 PathManifest Name
src/handlers/hello.tshello.json
src/handlers/users/[id].tsusers_[id].json
src/handlers/posts/[id]/comments.tsposts_[id]_comments.json
src/modules/database.tsdatabase.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 → /hello

Development 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:3000

2. File Changes ​

When you modify a file:

plaintext
📝 File changed: users/[id].ts
✅ Updated manifest: users_[id].json
🔄 Reloaded GET /users/:id

3. New Files ​

When you add a file:

plaintext
📄 File added: posts/create.ts
✅ Created manifest: posts_create.json
🆕 Registered POST /posts/create

4. Deleted Files ​

When you delete a file:

plaintext
🗑️ File removed: old-handler.ts
🗑️ Removed manifest: old-handler.json
❌ Unregistered GET /old-handler

Performance Benefits ​

Traditional Approach ​

plaintext
File Change → Full Project Scan → Rebuild All Routes → Restart Server
Time: ~2-5 seconds

Gati Manifest System ​

plaintext
File Change → Update Single Manifest → Reload Changed Route
Time: ~50-200ms

Benchmarks ​

  • 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 file

Missing Exports ​

plaintext
⚠️ No handler export found in users.ts
⚠️ Skipping file without handler

Route 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-watch

Custom 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].json

View 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.ts

Troubleshooting ​

Manifests Not Updating ​

  1. Check file permissions on .gati/ directory
  2. Ensure TypeScript syntax is valid
  3. Verify exports are present

Routes Not Loading ​

  1. Check _app.json contains your route
  2. Verify handler export name matches manifest
  3. Check for route conflicts in logs

Performance Issues ​

  1. Reduce file watching scope
  2. Use .gitignore patterns to exclude files
  3. 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 ​

Released under the MIT License.