Ensuring type safety and cache consistency with SHA-1 hashing
FlagFlow implements a sophisticated hash-based validation system that ensures your client-side TypeScript types remain in sync with server-side flag schemas. This prevents runtime errors and provides automatic cache invalidation when flag schemas change.
Core Concept: Each flag group has a unique SHA-1 hash based on its structure. When schemas change, hashes change, automatically invalidating outdated client code.
FlagFlow generates SHA-1 hashes for each flag group using a combination of:
Flags are organized hierarchically using forward slashes as separators. Each level gets its own hash:
// Example flag structure and their hashes accounting/ -> HASH_FLAGFLOW__ACCOUNTING accounting/huf/ -> HASH_FLAGFLOW__ACCOUNTING__HUF accounting/huf/allow_exchange -> Individual flag in group // Generated hash constants export const HASH_FLAGFLOW = '2a3e205e597f33fd493563b6db4543a40e1ec986'; export const HASH_FLAGFLOW__ACCOUNTING = 'a004c5733c25be5995e918335aa777b1c5c0d4f4'; export const HASH_FLAGFLOW__ACCOUNTING__HUF = 'b12f456a789c012d345e678f901a234b567c890d';
The validation process uses HTTP headers for hash exchange:
Header | Direction | Purpose |
---|---|---|
x-accept-flaggroup-hash | Client → Server | Expected hash from client's perspective |
x-flaggroup-hash | Server → Client | Current hash from server's perspective |
When you generate TypeScript definitions from FlagFlow, hash constants are automatically included for use in your client applications:
import axios from "axios"; import { flagFlow_Descriptors, FlagFlow_DescriptorTypeMap } from "./flagflowTypes"; const FLAGFLOW_BASE_URL = 'http://localhost:3000/flags'; export const fetchData = async <K extends keyof FlagFlow_DescriptorTypeMap>( key: K ): Promise<FlagFlow_DescriptorTypeMap[K]> => { const { uri, hash } = flagFlow_Descriptors[key]; const { data } = await axios.get<FlagFlow_DescriptorTypeMap[K]>( FLAGFLOW_BASE_URL + uri, { responseType: 'json', headers: { 'Content-Type': 'application/json', 'x-accept-flaggroup-hash': hash, // <- Hash validation } }) return data }
The generated TypeScript includes a descriptor map that connects flag groups to their URIs and hashes:
export const flagFlow_Descriptors = { '#root': { uri: '/', hash: HASH_FLAGFLOW }, 'accounting': { uri: '/accounting', hash: HASH_FLAGFLOW__ACCOUNTING }, 'accounting/huf': { uri: '/accounting/huf', hash: HASH_FLAGFLOW__ACCOUNTING__HUF } } as const;
When hashes don't match, FlagFlow returns a 409 Conflict
status with detailed information:
// Server response when hashes don't match HTTP/1.1 409 Conflict Content-Type: application/json { "error": "Hash mismatch detected", "details": { "expected": "2a3e205e597f33fd493563b6db4543a40e1ec986", "actual": "3b4f306f608g44ge504674c7ec5654b51f2fd097", "group": "accounting/huf", "message": "Flag schema has changed. Please update your TypeScript definitions." } }
export const fetchDataWithErrorHandling = async <K extends keyof FlagFlow_DescriptorTypeMap>( key: K ): Promise<FlagFlow_DescriptorTypeMap[K]> => { try { return await fetchData(key); } catch (error) { if (error.response?.status === 409) { console.error('🚨 Schema hash mismatch!'); console.error('Please regenerate your TypeScript definitions:'); console.error('curl http://localhost:3000/type/typescript > flagflowTypes.ts'); throw new Error('Flag schema out of date. Please update TypeScript definitions.'); } throw error; } }
FlagFlow provides dedicated endpoints for working with hashes:
Endpoint | Description | Response |
---|---|---|
GET /type/hash | Get hash map for all flag groups | JSON with group → hash mapping |
GET /flags/[group] | Get flag values with hash validation | Flag data or 409 if hash mismatch |
// Get current hash map curl http://localhost:3000/type/hash // Response { "#root": "2a3e205e597f33fd493563b6db4543a40e1ec986", "accounting": "a004c5733c25be5995e918335aa777b1c5c0d4f4", "accounting/huf": "b12f456a789c012d345e678f901a234b567c890d" }
#!/bin/bash # update-flagflow-types.sh echo "🔄 Checking for FlagFlow schema changes..." # Download current TypeScript definitions curl -s http://localhost:3000/type/typescript > flagflowTypes.new.ts # Compare with existing file if ! cmp -s flagflowTypes.ts flagflowTypes.new.ts; then echo "📝 Schema changes detected, updating types..." mv flagflowTypes.new.ts flagflowTypes.ts echo "✅ TypeScript definitions updated!" else echo "✅ No schema changes detected." rm flagflowTypes.new.ts fi
# In your CI pipeline (e.g., GitHub Actions) name: Check FlagFlow Schema on: [push, pull_request] jobs: check-flagflow: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Check FlagFlow schema sync run: | # Download current schema curl $FLAGFLOW_URL/type/typescript > current-schema.ts # Compare with committed version if ! cmp -s flagflowTypes.ts current-schema.ts; then echo "❌ FlagFlow schema out of sync!" echo "Run: curl $FLAGFLOW_URL/type/typescript > flagflowTypes.ts" exit 1 fi echo "✅ FlagFlow schema is up to date"
/type/typescript