mcpField
H
har-replay

har-replay

github.com/lightdashテスト・品質6.2k372 回インストール今日更新
リポジトリ

har-replay について

Lightdashのフロントエンド開発者向けのHAR再生スキルです。HARファイルを一時的なモックバックエンドに変換し、データベース、データウェアハウス、認証なしで記録済みデータを使ってページを表示します。

主な機能

  1. 01HARのリクエストとページ情報を分析
  2. 02パスとリクエストボディで照合
  3. 03レスポンスをデコードしステータスを正規化
  4. 04一時的なNode.js再生サーバーを作成
  5. 05フロントエンドを起動してページを確認

活用シーン

  1. 01記録済みデータでフロントエンドを再現
  2. 02データベースなしでダッシュボードを確認
  3. 03HARの不足エントリーや応答形式を調査

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/har-replayを入力して読み込まれたか確認します。

詳細

HAR リプレイ

HAR ファイルをモックの Lightdash バックエンドとして再生し、本番環境と完全に同じデータでフロントエンドを表示します。データベース、ウェアハウス、認証は不要です。

ユーザーから .har ファイルのパスを指定してもらう必要があります。$ARGUMENTS として渡されていない場合は、パスを尋ねてください。

Step 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)

Step 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 に正規化:HAR には本文全体が含まれているため、304 応答は 200 として返します
  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 を使用します。該当するエントリーが 1 つだけなら、パスの完全一致で照合できます。
  • その他の POST リクエスト(例:availableFilters):通常はパスが固有のため、完全一致で照合できます。
  • 分析で同じパスへの複数エントリーが確認された POST エンドポイント:識別用フィールドを使った本文ベースの照合が必要です。
サーバーの動作
  • ポート 3001 で待ち受けます
  • すべてのリクエストについて、メソッド、パス、照合結果をログに記録します
  • 404 の場合は {"status":"error","error":{"message":"HAR replay: no matching entry"}} を返します
  • 起動時に、インデックス化した応答数と、移動先となるダッシュボード URL を表示します

Step 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
    

Step 4: URL を提示する

両方のサーバーが稼働していることを確認したら、ブラウザーで開く URL をユーザーに伝えます。Step 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 の起動出力で実際のポートを確認してください。

Step 5: レンダリングエラーをデバッグする

ユーザーからエラーが報告された場合:

  1. リプレイサーバーのログで 404(HAR エントリーの不足)がないか確認します
  2. 応答形式に問題がないか確認します(Base64 エンコーディング、予期しない MIME タイプなど)
  3. リプレイサーバーのスクリプトを修正して再起動します

注記

  • リプレイサーバーは使い捨てです。特定の HAR ファイルに合わせて作成されるため、使用後に削除できます
  • HAR ファイルにはセッション Cookie や機密データが含まれる場合があります。コミットしないでください
  • キャプチャしたページから別のページへ移動すると、フロントエンドは 404 を返します
  • 再キャプチャするには、Chrome DevTools の Network タブで右クリックし、「Save all as HAR with content」を選択します

コメント