mcpField
X
xvcl

xvcl

github.com/fastly開発3640 回インストール1 週間前に更新
リポジトリ

xvcl について

XVCLを使ってFastly VCLを書くためのスキルです。コンパイル時メタプログラミングを加えるトランスパイラーを利用し、.xvclの作成、VCLへの変換、Falcoによるローカルテストを支援します。

主な機能

  1. 01コンパイル時定数と式
  2. 02ループ、条件分岐、テンプレート
  3. 03関数とインラインマクロ
  4. 04循環検出付きインクルード
  5. 05.xvclから.vclへのコンパイル
  6. 06FalcoによるVCLのローカルシミュレーション

活用シーン

  1. 01バックエンドやルーティング設定を反復生成
  2. 02データ駆動型のVCLテーブルを作成
  3. 03Fastly VCLの変更をローカルでコンパイル・シミュレーション

インストール

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

$npx skills add https://github.com/fastly/fastly-agent-toolkit --skill xvcl

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
backends.md
caching.md
crypto.md
headers.md
strings.md
subroutines.md
tables-acls.md
testing-vcl.md
vcl-syntax.md
xvcl-directives.md

詳細

トリガーと対象範囲

次の場合にトリガーします:XVCL、.xvcl ファイル、VCL トランスパイラー、VCL メタプログラミング、VCL のコンテキストでの #const/#for/#def/#inline、VCL スクリプトの作成、VCL の作成とローカルでの実行、または Fastly VCL の作成タスク全般。

次の場合はトリガーしないでください:XVCL を使わない既存の .vcl ファイルのデバッグ、Fastly API/CLI の操作、Fastly Compute、Terraform(VCL に言及している場合も含む)。

ユーザーがプレーンな VCL を明示的にリクエストした場合は、.vcl ファイルを使用し、XVCL を導入しないでください。 デフォルトの例よりも、明示的に指定されたツールとテストモードを優先してください。

XVCL を使った VCL の作成

XVCL は、Fastly VCL にメタプログラミング機能を追加する VCL トランスパイラーです。.xvcl ファイルを作成し、.vcl にコンパイルしてから、Falco でテストするか Fastly にデプロイします。XVCL の構文はすべてコンパイル時に解決されるため、実行時のオーバーヘッドはありません。

クイックスタート

コンパイルには、uv が提供する uvx が必要です。ローカルでのチェックには Falco を使用します。

# Compile with uvx
uvx xvcl main.xvcl -o main.vcl

# Lint the output
falco lint main.vcl

# Run locally — this is how you "run" VCL on your machine
falco simulate main.vcl
# Listens on localhost:3124 by default
# Then test with: curl http://localhost:3124/

別のテストモードを明示的にリクエストされない限り、ローカル実行ではコンパイルして falco simulate を実行してください。 Lint のみでは VCL は実行されません。

最小限の動作例

バックエンドの命名:Fastly VCL では、バックエンド名に F_ プレフィックスが必要です(例:F_origin、F_api)。backend default は使用しないでください。falco に拒否されます。vcl_recv では必ず req.backend を明示的に設定してください。

#const ORIGIN_HOST = "api.example.com"
#const REGIONS = [("us", "us.example.com"), ("eu", "eu.example.com")]

#for name, host in REGIONS
backend F_{{name}} {
  .host = "{{host}}";
  .port = "443";
  .ssl = true;
}
#endfor

sub vcl_recv {
  #FASTLY recv
  set req.backend = F_us;
  return (lookup);
}

sub vcl_deliver {
  #FASTLY deliver
  set resp.http.X-Served-By = "edge";
  return (deliver);
}

XVCL ディレクティブの概要

すべてのディレクティブの構文と例については、xvcl-directives.md を参照してください。

定数 — #const
#const NAME = value              // type auto-inferred
#const NAME TYPE = value         // explicit type
#const TTL INTEGER = 3600
#const ORIGIN = "origin.example.com"
#const ENABLED BOOL = true
#const DOUBLE_TTL = TTL * 2      // expressions supported
#const BACKENDS = ["web1", "web2"] // lists
#const PAIRS = [("api", 8080), ("web", 80)] // tuples

定数はコンパイル時のみ使用され、VCL 変数にはなりません。値を出力するには、必ず {{NAME}} を使用してください。VCL 内で定数名だけを記述すると(例:error 200 GREETING;)、リテラル文字列としてそのまま出力され、無効な VCL になります。代わりに error 200 "{{GREETING}}"; を使用してください。

テンプレートでの使用例:"{{TTL}}"、{{ORIGIN}}、backend F_{{name}} { ... }

テンプレート式 — {{ }}
{{CONST_NAME}}                   // constant substitution
{{PORT * 2}}                     // arithmetic
{{hex(255)}}                     // → "0xff"
{{format(42, '05d')}}            // → "00042"
{{len(BACKENDS)}}                // list length
{{value if condition else other}} // ternary

組み込み関数:range()、len()、str()、int()、hex()、format()、enumerate()、min()、max()、abs()

For ループ — #for / #endfor
#for i in range(5)               // 0..4
#for i in range(2, 8)            // 2..7
#for item in LIST_CONST          // iterate list
#for name, host in TUPLES        // tuple unpacking
#for idx, item in enumerate(LIST) // index + value
ループを使ったテーブル

インラインの if チェーンを生成する代わりに、#for ループを使って VCL の table 宣言に値を設定し、O(1) のルックアップを実現してください。

#const REDIRECTS = [
  ("/blog", "/articles"),
  ("/about-us", "/about"),
  ("/products/old-widget", "/products/widget-v2")
]

// O(1) hash-table lookup — the right pattern for data-driven VCL
table redirects STRING {
#for old_path, new_path in REDIRECTS
  "{{old_path}}": "{{new_path}}",
#endfor
}

sub vcl_recv {
  if (table.contains(redirects, req.url.path)) {
    error 801 table.lookup(redirects, req.url.path);
  }
}

インラインの if チェーンを生成するよりも、#for ループを使って VCL の table 宣言に値を設定する方法を推奨します。テーブルは O(1) のハッシュルックアップを実現し、データに基づくルーティング、リダイレクト、設定全般に適した Fastly の標準的なパターンです。

条件分岐 — #if / #elif / #else / #endif
#if PRODUCTION
  set req.http.X-Env = "prod";
#elif STAGING
  set req.http.X-Env = "staging";
#else
  set req.http.X-Env = "dev";
#endif

対応するもの:ブール定数、比較演算子(==、!=、<、>)、論理演算子(and、or、not)。

変数の省略記法 — #let
#let cache_key STRING = req.url.path;
// expands to:
// declare local var.cache_key STRING;
// set var.cache_key = req.url.path;
関数 — #def / #enddef
// Single return value
#def normalize_path(path STRING) -> STRING
  declare local var.result STRING;
  set var.result = std.tolower(path);
  return var.result;
#enddef

// Tuple return (multiple values)
#def parse_pair(s STRING) -> (STRING, STRING)
  declare local var.key STRING;
  declare local var.value STRING;
  set var.key = regsub(s, ":.*", "");
  set var.value = regsub(s, "^[^:]*:", "");
  return var.key, var.value;
#enddef

// Call sites
set var.clean = normalize_path(req.url.path);
set var.k, var.v = parse_pair("host:example.com");

関数は VCL サブルーチンにコンパイルされ、パラメーターは req.http.X-Func-* ヘッダーを介して渡されます。

インラインマクロ — #inline / #endinline
#inline cache_key(url, host)
digest.hash_md5(url + "|" + host)
#endinline

// Zero-overhead text substitution. Auto-parenthesizes arguments
// containing operators to prevent precedence bugs.
set req.hash += cache_key(req.url, req.http.Host);
インクルード — #include
#include "includes/backends.xvcl"  // relative path
#include <stdlib/security.xvcl>    // include path (-I)

インクルードは一度だけ実行されます。循環インクルードは検出され、報告されます。

コンパイル

# Basic
uvx xvcl input.xvcl -o output.vcl

# With include paths
uvx xvcl main.xvcl -o main.vcl -I ./includes -I ./shared

# Debug mode (shows expansion traces)
uvx xvcl main.xvcl -o main.vcl --debug

# Source maps (adds BEGIN/END INCLUDE markers)
uvx xvcl main.xvcl -o main.vcl --source-maps
オプション説明
-o, --output出力ファイル(デフォルト:.xvcl を .vcl に置き換え)
-I, --includeインクルード検索パスを追加(複数指定可能)
--debug / -v展開トレースを表示
--source-mapsソース位置のコメントを追加
--error-formatエラー出力形式:text(デフォルト)または json

よくある間違い

  • VCL で定数名をそのまま使う:error 200 GREETING; はリテラル文字列としてそのまま通ります。テンプレート構文を使い、error 200 "{{GREETING}}"; と記述してください。
  • テーブルではなく if チェーンを生成する:データ駆動型のルーティングやリダイレクトでは、インラインの if チェーンではなく、必ず #for を使って VCL の table に値を設定してください。if チェーンは O(n)、テーブルは O(1) です。
  • #FASTLY マクロを忘れる:すべての VCL サブルーチン(vcl_recv、vcl_fetch、vcl_deliver、vcl_error、vcl_hit、vcl_miss、vcl_pass)の先頭に、#FASTLY recv(または対応する名前)が必要です。
  • backend default を使う:Fastly VCL では、バックエンド名に F_ のプレフィックスが必要です。backend F_origin { ... } を使い、set req.backend = F_origin; と記述してください。

VCL の注意点

間違いやすい VCL ランタイムの落とし穴:

  • 剰余演算子はありません:VCL には % 演算子がありません。トラフィックを分割するには、ハッシュダイジェストに対して substr() を使います。if (substr(digest.hash_sha256(client.ip), 0, 1) ~ "^[0-7]$") は約 50%になります。または randomint(0, 99) < 50 を使ってください。
  • Vary は必ず vcl_fetch で設定する:Vary ヘッダーはキャッシュキーを制御します。vcl_deliver でのみ設定するのでは遅すぎます。Vary のディメンションを含めずに、すでにオブジェクトがキャッシュされているためです。必ず vcl_fetch で Vary を追加してください(クライアントから見えるヘッダーのために、必要に応じて vcl_deliver にも同じ内容を設定できます)。既存の Vary を上書きしないでください。確認してから追加します。
  • falco テストでは req.url.path は読み取り専用です:テスト用サブルーチンでは、set req.url.path = "/path" ではなく set req.url = "/path" を使ってください。.path プロパティは req.url から計算されるため、直接設定できません。
  • req.request は非推奨です:代わりに req.method を使ってください。Falco はどちらも受け付けますが、req.method が現在の形式です。
  • Cookie の解析:正規表現ではなく、subfield(req.http.Cookie, "name", ";") を使ってください。Cookie ~ "name=(\w+)" のような正規表現では、似たプレフィックスの Cookie(例:name_v2=X)に誤って一致することがあります。

参考資料

VCL の基本(リクエストのライフサイクル、return アクション、変数の型)については、以下の VCL 構文およびサブルーチンの参考資料を参照してください。

特定の機能を実装する前に、該当する参考ファイルを最後まで読んでください。

トピックファイル使用する場面
XVCL ディレクティブxvcl-directives.mdXVCL コードを記述する場合 — すべてのディレクティブの完全な構文
VCL 構文vcl-syntax.mdデータ型、演算子、制御フローを扱う場合
サブルーチンsubroutines.mdリクエストのライフサイクルやカスタムサブルーチンを理解する場合
ヘッダーheaders.mdHTTP ヘッダーを操作する場合
バックエンドbackends.mdオリジン、ディレクター、ヘルスチェックを設定する場合
キャッシュcaching.mdTTL、猶予期間、キャッシュキーを設定する場合
文字列strings.md文字列操作関数を使う場合
暗号crypto.mdハッシュ化、HMAC、base64 エンコードを行う場合
テーブル/ACLtables-acls.mdルックアップテーブル、アクセス制御リストを使う場合
VCL のテストtesting-vcl.md単体テスト、アサーション、テストヘルパーを作成する場合

プロジェクト構成

vcl/
├── main.xvcl
├── config.xvcl          # shared constants
└── includes/
    ├── backends.xvcl
    ├── security.xvcl
    ├── routing.xvcl
    └── caching.xvcl

コメント