#!/bin/bash # Structured progress emit for migrate flows. Phase 1's WebUI streams these # JSON-per-line records and renders them. When MIGRATE_JSON_PROGRESS=1 the # helper writes machine-readable JSON to fd 3 (or stdout if fd 3 isn't open); # otherwise it's a no-op so the human CLI output stays clean. # # Usage: # migrateEmit phase=preflight status=running # migrateEmit phase=restore status=running pct=50 # migrateEmit phase=done status=complete duration_seconds=42 # # Keys with spaces or special chars should be passed pre-quoted (a=foo\ bar) # or via the explicit setter form (rare — most callers use the simple kv form). migrateEmit() { [[ "$MIGRATE_JSON_PROGRESS" != "1" ]] && return 0 local pair local first=1 local out='{' for pair in "$@"; do local k="${pair%%=*}" local v="${pair#*=}" # Numeric values stay bare; everything else gets quoted + escaped. local rendered if [[ "$v" =~ ^-?[0-9]+(\.[0-9]+)?$ ]]; then rendered="$v" else local escaped="${v//\\/\\\\}" escaped="${escaped//\"/\\\"}" escaped="${escaped//$'\n'/\\n}" escaped="${escaped//$'\t'/\\t}" rendered="\"$escaped\"" fi if (( first )); then out+="\"$k\":$rendered" first=0 else out+=",\"$k\":$rendered" fi done out+='}' # fd 3 is the structured channel — Phase 1 opens it. If it's not open # (interactive CLI), fall back to stdout so dev/debug runs still see it. if { true >&3; } 2>/dev/null; then printf '%s\n' "$out" >&3 else printf '%s\n' "$out" fi }