Skip to content

Instantly share code, notes, and snippets.

@adelin-b
Created March 3, 2026 00:09
Show Gist options
  • Select an option

  • Save adelin-b/cd86702901e7bd6562b9887c0170b7d4 to your computer and use it in GitHub Desktop.

Select an option

Save adelin-b/cd86702901e7bd6562b9887c0170b7d4 to your computer and use it in GitHub Desktop.
Live Watcher Demo: Backend Type Change → Frontend Error Detection
{"version":3,"term":{"cols":80,"rows":24,"type":"alacritty"},"timestamp":1772492035,"command":"/bin/bash scripts/demo-live-watcher.sh","title":"Live Watcher: Backend Type Change → Frontend Error","env":{"SHELL":"/bin/zsh"}}
[0.006, "o", "\r\n\u001b[44m\u001b[1;37m \u001b[0m\r\n"]
[0.000, "o", "\u001b[44m\u001b[1;37m LIVE WATCHER DEMO — Backend Type Change → Frontend Error \u001b[0m\r\n\u001b[44m\u001b[1;37m \u001b[0m\r\n\r\n"]
[2.007, "o", "\r\n"]
[0.000, "o", " \u001b[1;35m╭──────────────────────────────────────────────────────────────╮\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m│\u001b[0m \u001b[1;36mSTEP 1\u001b[0m \u001b[1;37mVerify types are currently in sync\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m╰──────────────────────────────────────────────────────────────╯\u001b[0m\r\n\r\n"]
[0.000, "o", " \u001b[2m$\u001b[0m \u001b[1;32mnpx tsc --noEmit\u001b[0m\r\n\r\n"]
[1.426, "o", " \u001b[42m\u001b[1;37m ✓ \u001b[0m \u001b[1;32mZero TypeScript errors — backend and frontend types match\u001b[0m\r\n"]
[3.007, "o", "\r\n"]
[0.000, "o", " \u001b[1;35m╭──────────────────────────────────────────────────────────────╮\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m│\u001b[0m \u001b[1;36mSTEP 2\u001b[0m \u001b[1;37mStart the backend type watcher\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m╰──────────────────────────────────────────────────────────────╯\u001b[0m\r\n\r\n"]
[0.000, "o", " \u001b[2m$\u001b[0m \u001b[1;32mnode scripts/watch-backend-types.mjs &\u001b[0m\r\n\r\n"]
[0.080, "o", "[watch-types] Watching /Users/adelinb/Documents/Projects/ChatVote/CHATVOTE-BackEnd/src/models\r\n"]
[0.929, "o", "\r\n"]
[0.000, "o", " \u001b[1;33m▸\u001b[0m Watcher monitoring \u001b[1;36mCHATVOTE-BackEnd/src/models/\u001b[0m for changes\r\n"]
[2.007, "o", "\r\n"]
[0.000, "o", " \u001b[1;35m╭──────────────────────────────────────────────────────────────╮\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m│\u001b[0m \u001b[1;36mSTEP 3\u001b[0m \u001b[1;37mCurrent PartyResponseChunkDto in backend\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m╰──────────────────────────────────────────────────────────────╯\u001b[0m\r\n\r\n"]
[0.000, "o", " \u001b[2m$\u001b[0m \u001b[1;32mgrep -A12 'class PartyResponseChunkDto' dtos.py\u001b[0m\r\n"]
[0.000, "o", "\r\n"]
[0.000, "o", "\u001b[2m\r\n"]
[0.004, "o", "class PartyResponseChunkDto(BaseModel):\r\n session_id: str = Field(\r\n ..., description=\"The ID of the chat session to which the message belongs\"\r\n )\r\n party_id: Optional[str] = Field(\r\n ...,\r\n description=\"The ID of the party the message is coming from. None for general Perplexity chat\",\r\n )\r\n chunk_index: int = Field(..., description=\"The index of the chunk in the response\")\r\n chunk_content: str = Field(..., description=\"The message content\")\r\n"]
[0.000, "o", " is_end: bool = Field(\r\n ..., description=\"Whether this is the last chunk of the response\"\r\n )\r\n"]
[0.001, "o", "\u001b[0m\r\n \u001b[1;33m▸\u001b[0m Note the field: \u001b[1;37mchunk_content\u001b[0m: str\r\n"]
[3.007, "o", "\r\n"]
[0.000, "o", " \u001b[1;35m╭──────────────────────────────────────────────────────────────╮\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m│\u001b[0m \u001b[1;36mSTEP 4\u001b[0m \u001b[1;37mRename field: chunk_content → text_content\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m╰──────────────────────────────────────────────────────────────╯\u001b[0m\r\n\r\n"]
[0.000, "o", " \u001b[2m$\u001b[0m \u001b[1;32msed -i '' 's/chunk_content: str/text_content: str/' dtos.py\u001b[0m\r\n\r\n"]
[0.005, "o", " \u001b[1;33m⏳ Waiting for watcher to detect the change...\u001b[0m\r\n\r\n"]
[0.010, "o", "[watch-types] .!95911!dtos.py changed, regenerating...\r\n"]
[0.001, "o", "[watch-types] dtos.py changed, regenerating...\r\n"]
[0.000, "o", "[watch-types] dtos.py changed, regenerating...\r\n"]
[1.436, "o", "[watch-types] Regenerated in 1134ms\r\n"]
[2.562, "o", "\r\n \u001b[42m\u001b[1;37m ✓ \u001b[0m \u001b[1;32mWatcher detected change and regenerated types automatically!\u001b[0m\r\n"]
[2.010, "o", "\r\n"]
[0.000, "o", " \u001b[1;35m╭──────────────────────────────────────────────────────────────╮\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m│\u001b[0m \u001b[1;36mSTEP 5\u001b[0m \u001b[1;37mGenerated TypeScript type has changed\u001b[0m\r\n \u001b[1;35m╰──────────────────────────────────────────────────────────────╯\u001b[0m\r\n"]
[0.000, "o", "\r\n"]
[0.000, "o", " \u001b[2m$\u001b[0m \u001b[1;32mgrep -A7 'interface PartyResponseChunkDto' backend-types.generated.ts\u001b[0m\r\n\r\n"]
[0.010, "o", " \u001b[2mexport interface PartyResponseChunkDto {\u001b[0m\r\n"]
[0.005, "o", " \u001b[2m session_id: string;\u001b[0m\r\n"]
[0.005, "o", " \u001b[2m party_id: string | null;\u001b[0m\r\n"]
[0.005, "o", " \u001b[2m chunk_index: number;\u001b[0m\r\n"]
[0.004, "o", " \u001b[1;31m\u001b[1m text_content: string;\u001b[0m \u001b[1;33m← was 'chunk_content'\u001b[0m\r\n"]
[0.004, "o", " \u001b[2m is_end: boolean;\u001b[0m\r\n"]
[0.002, "o", " \u001b[2m}\u001b[0m\r\n"]
[0.003, "o", " \u001b[2m\u001b[0m\r\n"]
[0.000, "o", "\r\n"]
[3.007, "o", "\r\n"]
[0.000, "o", " \u001b[1;35m╭──────────────────────────────────────────────────────────────╮\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;35m│\u001b[0m \u001b[1;36mSTEP 6\u001b[0m \u001b[1;37mTypeScript catches the drift!\u001b[0m\r\n \u001b[1;35m╰──────────────────────────────────────────────────────────────╯\u001b[0m\r\n"]
[0.000, "o", "\r\n"]
[0.000, "o", " \u001b[2m$\u001b[0m \u001b[1;32mnpx tsc --noEmit\u001b[0m\r\n\r\n"]
[5.426, "o", " \u001b[1;31m✗\u001b[0m \u001b[1;36msrc/lib/stores/actions/merge-streaming-chunk-payload-for-message.ts\u001b[0m:\u001b[1;33m17\u001b[0m \u001b[1;31mTS2339\u001b[0m: \u001b[1;37mProperty 'chunk_content' does not exist on type 'PartyResponseChunkDto'.\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;31m✗\u001b[0m \u001b[1;36msrc/lib/stores/actions/merge-streaming-chunk-payload-for-message.ts\u001b[0m:\u001b[1;33m28\u001b[0m \u001b[1;31mTS2339\u001b[0m: \u001b[1;37mProperty 'chunk_content' does not exist on type 'PartyResponseChunkDto'.\u001b[0m\r\n"]
[0.001, "o", " \u001b[1;31m✗\u001b[0m \u001b[1;36msrc/lib/stores/actions/merge-streaming-chunk-payload-for-message.ts\u001b[0m:\u001b[1;33m29\u001b[0m \u001b[1;31mTS2339\u001b[0m: \u001b[1;37mProperty 'chunk_content' does not exist on type 'PartyResponseChunkDto'.\u001b[0m\r\n"]
[0.000, "o", "\r\n"]
[0.000, "o", " \u001b[41m\u001b[1;37m ✗ \u001b[0m \u001b[1;31m3 errors — every usage of the old 'chunk_content' field caught!\u001b[0m\r\n\r\n"]
[0.000, "o", " \u001b[1;33m▸\u001b[0m File: \u001b[1;36msrc/lib/stores/actions/merge-streaming-chunk-payload-for-message.ts\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;33m▸\u001b[0m Property \u001b[1;31m'chunk_content'\u001b[0m no longer exists on \u001b[1;37mPartyResponseChunkDto\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;33m▸\u001b[0m It was renamed to \u001b[1;32m'text_content'\u001b[0m in the backend\r\n"]
[4.007, "o", "\r\n"]
[0.000, "o", "\u001b[42m\u001b[1;37m \u001b[0m\r\n"]
[0.000, "o", "\u001b[42m\u001b[1;37m ✓ DEMO COMPLETE \u001b[0m\r\n"]
[0.000, "o", "\u001b[42m\u001b[1;37m \u001b[0m\r\n\r\n"]
[0.000, "o", " \u001b[1;32m1.\u001b[0m Backend Pydantic model changed \u001b[2m(rename field in dtos.py)\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;32m2.\u001b[0m Watcher auto-regenerated TS types \u001b[2m(~1.4 seconds)\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;32m3.\u001b[0m TypeScript caught \u001b[1;31m3 errors\u001b[0m \u001b[2m(compile-time, not runtime!)\u001b[0m\r\n"]
[0.000, "o", " \u001b[1;32m4.\u001b[0m No silent bugs possible \u001b[2m(drift = instant tsc error)\u001b[0m\r\n\r\n"]
[4.007, "o", " \u001b[2m━━━ Cleanup ━━━\u001b[0m\r\n"]
[0.082, "o", "[generate-types] Running Python schema exporter...\r\n"]
[1.097, "o", "[generate-types] Converting 36 schemas...\r\n"]
[0.000, "o", "[generate-types] Written to /Users/adelinb/Documents/Projects/ChatVote/CHATVOTE-FrontEnd/src/lib/generated/backend-types.generated.ts\r\n[generate-types] Generated 36 types + event maps\r\n"]
[0.003, "o", " \u001b[2mReverted to clean state. ✓\u001b[0m\r\n\r\n"]
[0.000, "x", "0"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment