mcpField
H
har-replay

har-replay

github.com/lightdash测试与质量6.2k372 次安装今天更新
代码仓库

关于 har-replay

面向 Lightdash 前端开发的 HAR 重放技能,可将 HAR 文件转换为临时模拟后端,让页面使用捕获的生产数据渲染,无需数据库、数仓或身份验证。

主要功能

  1. 01分析 HAR 请求与页面信息
  2. 02按请求路径及请求体匹配
  3. 03解码响应并处理状态码
  4. 04生成临时 Node.js 重放服务
  5. 05运行前端并验证页面

适用场景

  1. 01用捕获的数据复现前端页面
  2. 02在无数据库环境下检查仪表板
  3. 03排查 HAR 缺失请求或响应格式问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lightdash/lightdash --skill har-replay

安装后在 Claude Code 中输入/har-replay确认已加载。

详细介绍

HAR 回放

将 HAR 文件回放为模拟的 Lightdash 后端,让前端使用完全一致的生产数据进行渲染。无需数据库、数据仓库或身份验证。

用户必须提供 .har 文件的路径。如果未通过 $ARGUMENTS 提供,请询问用户。

步骤 1:分析 HAR 文件

运行 Python 脚本,从 HAR 中提取关键信息:

import json, sys, re
from collections import Counter
from urllib.parse import urlparse

har_path = "$HAR_FILE_PATH"
with open(har_path) as f:
    har = json.load(f)

entries = har['log']['entries']

# Extract the page URL from the HAR pages section
page_path = None
pages = har['log'].get('pages', [])
for p in pages:
    title = p.get('title', '')
    parsed = urlparse(title)
    if parsed.path and parsed.path != '/':
        page_path = parsed.path
        break

# Fallback: extract from referer headers
if not page_path:
    for e in entries:
        for h in e['request']['headers']:
            if h['name'].lower() == 'referer':
                parsed = urlparse(h['value'])
                if parsed.path and parsed.path != '/':
                    page_path = parsed.path
                    break
        if page_path:
            break

# Find the origin (first API call)
origin = None
for e in entries:
    url = urlparse(e['request']['url'])
    if url.path.startswith('/api/'):
        origin = f"{url.scheme}://{url.netloc}"
        break

# Filter to origin entries only
api_entries = [e for e in entries if origin and origin in e['request']['url']]

# Find dashboard URL if present
dashboard_uuid = None
for e in api_entries:
    path = urlparse(e['request']['url']).path
    m = re.search(r'/dashboards/([0-9a-f-]{36})', path)
    if m:
        dashboard_uuid = m.group(1)
        break

# Find project UUID
project_uuid = None
for e in api_entries:
    path = urlparse(e['request']['url']).path
    m = re.search(r'/projects/([0-9a-f-]{36})', path)
    if m:
        project_uuid = m.group(1)
        break

# Count request types
methods = Counter()
api_paths = Counter()
has_base64 = False
post_endpoints_needing_body_match = []

for e in api_entries:
    method = e['request']['method']
    path = urlparse(e['request']['url']).path
    methods[method] += 1
    normalized = re.sub(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', '{uuid}', path)
    api_paths[f'{method} {normalized}'] += 1
    if e['response']['content'].get('encoding') == 'base64':
        has_base64 = True

# Find POST endpoints with multiple entries (need body-based matching)
for key, count in api_paths.items():
    if key.startswith('POST') and count > 1:
        post_endpoints_needing_body_match.append((key, count))

print(f"Page path: {page_path}")
print(f"Origin: {origin}")
print(f"Total API entries: {len(api_entries)}")
print(f"Project UUID: {project_uuid}")
print(f"Dashboard UUID: {dashboard_uuid}")
print(f"Has base64 content: {has_base64}")
print(f"Methods: {dict(methods)}")
print(f"POST endpoints needing body match: {post_endpoints_needing_body_match}")
print()
print("API paths:")
for p, c in sorted(api_paths.items()):
    print(f"  {p}: {c}")

向用户报告分析结果:

  • 从 HAR 中提取的页面路径(这是捕获 HAR 时用户正在查看的 URL)
  • 来源域名
  • API 条目数量
  • 需要基于请求体进行匹配的显著 POST 端点(例如,包含多个图表磁贴的 dashboard-chart)

步骤 2:编写定制的回放服务

根据分析结果,将回放服务写入 scripts/har-replay-server.ts。服务必须处理以下事项:

核心结构
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';

仅使用 Node.js 内置模块。使用 npx tsx scripts/har-replay-server.ts <har-path> 运行。

HAR 解析规则
  1. 按来源筛选:仅索引与检测到的来源域名匹配的条目
  2. Base64 解码:检查 entry.response.content.encoding === 'base64',并使用 Buffer.from(text, 'base64').toString('utf-8') 解码
  3. 跳过待处理的轮询响应:对于 GET /api/v2/projects/{uuid}/query/{uuid} 端点,解析响应体并跳过 results.status === 'pending' 的条目——仅保留最终的 ready 响应
  4. 将 304 规范化为 200:将 304 响应作为 200 提供(它们在 HAR 中包含完整响应体)
  5. 移除传输层标头:从响应标头中移除 :pseudo-headers、transfer-encoding、content-encoding、content-length
请求匹配策略
  • GET 请求:按精确的 "METHOD /path?query" 匹配。若匹配失败,则回退到不带查询字符串的路径。
  • POST dashboard-chart:这些请求都会访问同一个 URL,但请求体中的 chartUuid 不同。根据 HAR 请求体中的 chartUuid 建立索引,并通过解析传入请求的请求体进行匹配。
  • POST dashboard-sql-chart:采用相同模式,但使用 savedSqlUuid。如果只有一个条目,则按精确路径匹配即可。
  • 其他 POST 请求(例如 availableFilters):通常路径唯一,按精确路径匹配即可。
  • 分析结果中显示同一路径存在多个条目的任何 POST 端点:必须使用基于请求体中某个区分字段的匹配方式。
服务行为
  • 监听 3001 端口
  • 记录每个请求的方法、路径以及是否匹配
  • 对 404 响应返回 {"status":"error","error":{"message":"HAR replay: no matching entry"}}
  • 启动时打印索引的响应数量和要导航到的仪表板 URL

步骤 3:启动回放服务和前端

  1. 终止 3001 端口上的所有现有进程
  2. 在后台启动回放服务:
    npx tsx scripts/har-replay-server.ts <har-path>
    
  3. 启动 Vite 前端开发服务,并将其指向回放服务:
    PORT=3001 pnpm -F frontend dev
    
  4. 等待两个服务就绪,然后使用以下命令验证:
    curl -s http://localhost:3001/api/v1/health
    

步骤 4:提供 URL

确认两个服务都在运行后,告知用户要在浏览器中打开的 URL。根据步骤 1 提取的页面路径构造 URL:

http://localhost:<vite-port><page-path>

例如,如果 HAR 是在 /projects/abc-123/dashboards/def-456/view 页面捕获的,则 URL 为 http://localhost:3002/projects/abc-123/dashboards/def-456/view。

注意:由于 3001 端口已被占用,Vite 可能会选择 3000 以外的端口。请检查 Vite 启动输出,确认实际使用的端口。

步骤 5:调试渲染错误

如果用户报告错误:

  1. 检查回放服务日志中是否有 404(缺少 HAR 条目)
  2. 检查响应格式问题(Base64 编码、意外的 MIME 类型)
  3. 修复回放服务脚本并重启

说明

  • 回放服务是一次性的——它是针对特定 HAR 文件定制的,用完后可以删除
  • HAR 文件可能包含会话 Cookie 和敏感数据——不要将其提交到代码库
  • 离开捕获页面后,前端会对任何其他导航请求返回 404
  • 如需重新捕获:Chrome DevTools > Network 标签页 > 右键单击 > “Save all as HAR with content”

评论

支持 Markdown