HyperGen

postMessageプロトコル

HyperGenホストアプリケーションとサンドボックス化されたiframe間で交換されるすべてのpostMessageタイプ。正確なJSONシェイプとオリジン検証ルール付き。

postMessageプロトコル

ホストアプリケーションとサンドボックス化されたiframeは、ブラウザのpostMessage APIを介して通信します。本セクションでは、すべてのメッセージタイプ、正確なJSONシェイプ、オリジン検証要件を定義します。

postMessageはホスト-iframe統合にのみ使用されます: テーマ設定、リサイズ通知、ナビゲーションインテント、データ交換、ライフサイクル管理。エージェント通信(SSEストリーミング、ユーザーアクション送信)はiframeとエージェントサーバー間でHTTP経由で直接行われます — SSEメッセージフォーマットを参照。

メッセージエンベロープ

すべてのHyperGen postMessageはtypeフィールドを持つJSONオブジェクトでなければなりません (MUST):

interface HyperGenMessage {
  type: string;
  [key: string]: unknown;
}

typeフィールドはhg:プレフィックスの文字列でなければなりません (MUST)。認識されないタイプのメッセージは黙って無視すべきです (SHOULD)。

オリジン検証

ホストがiframeからメッセージを受信する場合

ホストは受信メッセージが期待されるiframeから発信されたことを検証しなければなりません (MUST):

  1. ソースチェック: event.sourceがiframeのcontentWindowと等しくなければなりません (MUST)。これにより、メッセージが正しいiframeからのものであることが確認されます(複数のiframeが存在する場合に重要)。
  2. オリジンチェック: iframeオリジンが既知の場合、event.originをそれに対して検証すべきです (SHOULD)。iframeOrigin"*"の場合、オリジンチェックはスキップされます。
window.addEventListener("message", function(event) {
  // MUST: メッセージソースが自分のiframeか検証
  if (event.source !== iframe.contentWindow) return;

  // SHOULD: オリジンが既知の場合検証
  if (expectedOrigin !== "*" && event.origin !== expectedOrigin) return;

  // メッセージを処理...
});

iframeがホストからメッセージを受信する場合

ブートストラップドキュメントのpostMessageハンドラーは、特定のhostOriginが設定されている場合、オリジンを検証しなければなりません (MUST):

window.addEventListener("message", function(e) {
  if (hostOrigin !== "*" && e.origin !== hostOrigin) return;
  // メッセージを処理...
});

本番デプロイメントでは"*"ではなく明示的なオリジンを設定すべきです (SHOULD)。

ホストからiframeへのメッセージ

hg:theme — CSS変数注入

iframeの:root要素にCSSカスタムプロパティを注入または更新します。ホストのデザインシステムとiframeを同期するために使用されます。

interface HgThemeMessage {
  type: "hg:theme";
  /** CSSカスタムプロパティのキー-値ペア。キーは有効なCSSカスタムプロパティ名でなければならない (MUST)。 */
  vars: Record<string, string>;
}

動作:

  • iframeはvarsを反復し、各エントリに対してdocument.documentElement.style.setProperty(key, value)を呼び出さなければなりません (MUST)。
  • メッセージは加算的です — 指定された変数を更新し、以前に設定された変数は削除しません。
  • ホストはiframeの読み込み直後とホストテーマが変更された時(ダークモード切り替え時など)にhg:themeメッセージを送信すべきです (SHOULD)。

hg:destroy — クリーンアップシグナル

iframeにシャットダウンを指示します: SSE接続の閉鎖、オブザーバーの停止、リソースの解放。

interface HgDestroyMessage {
  type: "hg:destroy";
}

動作:

  • iframeはSSE接続を閉じるためにルート要素からsse-connect属性を削除しなければなりません (MUST)。
  • iframeはHTMX SSEの状態をクリーンアップするためにhtmx:sseCloseをトリガーすべきです (SHOULD)。
  • hg:destroy処理後、ホストは通常DOMからiframeを削除します。

iframeからホストへのメッセージ

hg:ready — iframe準備完了シグナル

iframeのブートストラップドキュメントが読み込まれ、postMessageブリッジが動作可能であることをホストに通知します。

interface HgReadyMessage {
  type: "hg:ready";
}

動作:

  • iframeはブートストラップドキュメントのスクリプトが実行された後にこのメッセージを送信してもよいです (MAY)。
  • ホストはiframeが受信準備完了するまでhg:themeの送信を遅延するためにこのシグナルを使用してもよいです (MAY)。
  • このメッセージはオプション (OPTIONAL) です。ホストはhg:readyより前にhg:themeが送信される場合も処理すべきです (SHOULD)(iframeのloadイベント経由など)。

hg:resize — コンテンツ高さ通知

iframeコンテンツのサイズが変更されたことをホストに通知し、ホストがiframe要素のサイズを適切に調整できるようにします。

interface HgResizeMessage {
  type: "hg:resize";
  /** コンテンツの高さ(ピクセル単位、document.documentElement.scrollHeight)。 */
  height: number;
  /** コンテンツの幅(ピクセル単位、オプション)。 */
  width?: number;
}

動作:

  • iframeはサイズ変更を検出するためにdocument.bodyResizeObserverを使用しなければなりません (MUST)。
  • サイズ変更ごとに、iframeは現在のscrollHeightを含むhg:resizeメッセージを送信しなければなりません (MUST)。
  • widthフィールドはオプション (OPTIONAL) です。幅情報が有用なレイアウトのために含まれます。
  • ホストは受信値を設定された最小・最大高さ間にクランプして、iframe要素のheightスタイルプロパティを設定することで自動リサイズすべきです (SHOULD)。

hg:navigate — ナビゲーションインテント

ホストアプリケーションにURLへのナビゲーションを要求します。iframeはホストを直接ナビゲートできません(allow-top-navigation sandboxパーミッションが拒否されているため)。

interface HgNavigateMessage {
  type: "hg:navigate";
  /** ターゲットURL。絶対パスまたはホストに対する相対パス。 */
  url: string;
}

動作:

  • エージェント生成HTMLはdata-hg-navigate属性を持つ要素を含めることでナビゲーションをトリガーできます。ブートストラップドキュメントのクリックハンドラーがこれらのクリックをインターセプトし、hg:navigateメッセージを送信します。
  • ホストはナビゲーション前にURLを検証しなければなりません (MUST)(信頼されないドメインへの絶対URLを拒否するなど)。

hg:data — 任意データペイロード

iframeからホストに構造化データを送信します。エージェント生成UIが結果、選択、状態をホストアプリケーションに通信するための汎用チャネルです。

interface HgDataMessage {
  type: "hg:data";
  /** 任意のJSONシリアライズ可能なペイロード。 */
  payload: unknown;
}

動作:

  • エージェント生成スクリプトはdocument上にhg:dataという名前のCustomEventをディスパッチできます。ブートストラップのイベントリスナーがそれをキャッチし、event.detailpayloadとしてホストに転送します。
  • payloadは任意のJSONシリアライズ可能な値です: オブジェクト、配列、文字列、数値、真偽値、またはnull。

完全なTypeScript型定義

TypeScriptを使用する実装者向けの完全なメッセージタイプセット:

// ホスト → iframe
type HostToIframeMessage =
  | { type: "hg:theme"; vars: Record<string, string> }
  | { type: "hg:destroy" };

// iframe → ホスト
type IframeToHostMessage =
  | { type: "hg:ready" }
  | { type: "hg:resize"; height: number; width?: number }
  | { type: "hg:navigate"; url: string }
  | { type: "hg:data"; payload: unknown };

// すべてのメッセージのユニオン
type HyperGenMessage = HostToIframeMessage | IframeToHostMessage;

メッセージフロー図

ホストアプリケーション                    サンドボックス化されたiframe
     │                                    │
     │◄──── hg:ready ────────────────────│  (iframe読み込み完了)
     │                                    │
     │──── hg:theme ─────────────────────►│  (CSS変数注入)
     │                                    │
     │                                    │  (SSEがHTMLをストリーム、ユーザーが操作)
     │                                    │
     │◄──── hg:resize ──────────────────│  (コンテンツが拡大)
     │                                    │
     │◄──── hg:navigate ────────────────│  (ユーザーがナビリンクをクリック)
     │                                    │
     │◄──── hg:data ────────────────────│  (ユーザーがアクションを確認)
     │                                    │
     │──── hg:destroy ───────────────────►│  (ホストがiframeを削除)
     │                                    │

拡張性

実装はここで指定されたもの以外の追加メッセージタイプを定義してもよいです (MAY)。カスタムメッセージタイプは衝突を避けるためにhg:プレフィックスの後に名前空間を続けなければなりません (MUST):

hg:x-myapp:custom-event

受信者は認識しないメッセージタイプを黙って無視しなければなりません (MUST)。

目次