ADR-0033: Node と npm の実行に WebContainers を採り、実行環境と画面の間をインターフェースで分ける¶
Status¶
承認
Context¶
訪問者が、ブラウザの中で npm のパッケージを入れ、複数の Node の HTTP サーバーを同時に動かす実行環境の方式を決める必要がある。
最初に出す題材は認可コードフローで、ブラウザ、クライアント、認可サーバーの間のリダイレクトと Cookie が通ることが条件になる。
- 訪問者のコードは訪問者のブラウザの中だけで動かし、サイトのサーバーに送らない。
- ADR-0008: 読者の identity を前提とする機能を作らず、バックエンドを持たない
- 実行環境の選び方は、有効な決定が無い。
- ADR-0030: 種別を記事の 1 つにし、構成を記事の形式で、シリーズをディレクトリで表す
- Sandpack のパッケージは Apache-2.0 だが、Sandpack が Node を動かす実行環境の本体(Nodebox)のライセンスは、Sustainable Use License 1.0 である。非営利と個人の利用を許している。
- WebContainers のライセンスについて、StackBlitz の 2 つのページの記述が食い違う。
- 利用規約の 1.5(c) は、本番か商用での利用に、プランか書面のライセンスを求める。
- 「Use in a production or commercial setting [...] requires an active plan or separate written license that includes it」
- 出典: StackBlitz Terms of Service
- Commercial Usage のページは「Licensing is required for production usage of the API in a commercial, for-profit setting.」と書く。
- 紹介のページは「Always free for Open Source」と書く。fukuemon.dev のリポジトリは公開で、コードと記事の本文を MIT License で公開している。
- 利用規約の 1.5(c) は、本番か商用での利用に、プランか書面のライセンスを求める。
- WebContainers の実行環境の本体は、StackBlitz の CDN から読み込む。自分のサイトから配れない。
- WebContainers は、cross-origin isolation を求める。
- WebContainers の対応は、デスクトップの Chromium 系が正式で、Safari と Firefox はベータである。モバイルのブラウザは、対応の一覧に無い。
Decision¶
1. 方式¶
Node と npm の実行に、WebContainers(@webcontainer/api)を採る。
比べた 3 つの方式のうち、Cookie を含む認可コードフローが、コードを変えずに最後まで通ったのは WebContainers だけのためである。
2. ライセンスの扱い¶
StackBlitz に問い合わせずに、WebContainers を使う。
- 利用規約の 1.5(c) は「production or commercial」の利用にライセンスを求め、Commercial Usage のページは「commercial, for-profit」の production に限って求める。2 つの読みは食い違う。
- fukuemon.dev は、収益のない個人のサイトである。Commercial Usage のページの読みでは、ライセンスが要らない。利用規約の読みでは、本番で公開するサイトとして、ライセンスが要る。
- 運営者は、Commercial Usage のページの読みで使うと判断した。
- リポジトリのコードと記事の本文は、MIT License で公開している。紹介のページの「Always free for Open Source」に当たる見込みを上げるためである。当たるかは、StackBlitz に確認していない。
StackBlitz が API の利用を止めたときは、実行環境を差し替える。
- 差し替え先の候補は、almostnode(MIT)である。
- almostnode では、
Set-Cookieがブラウザに届かず、サーバーの間のfetchが失敗する。Cookie を使う認可コードフローの記事(OIDC の記事)は、差し替えると動かなくなる。 - OIDC の記事を動かし直すには、almostnode に、Cookie、サーバーの間の通信、プロセスの分離の 3 つを追加する。作れることを確認したのは、Cookie だけである。
- SQL だけを動かす記事と、Go を動かす記事は、WebContainers を使わないので、止められても動く。
3. 実行環境と画面の間のインターフェース¶
WebContainers の API を呼ぶコードを、1 つのモジュールに閉じる。
画面のコードは、WebContainers の API を直接呼ばない。
実行環境と画面の間を、次の 5 つの操作を持つインターフェースで分ける。
- ファイルの読み書きと一覧
- プロセスの起動、入力、出力、終了
- 待ち受けたポートと、ブラウザから開ける URL の通知
- HTTP の記録の読み取り
- コンソールの記録の読み取り
実行環境を差し替えるときに、画面のコードを変えずに済ませるためである。
4. ブラウザから開ける URL¶
WebContainers は、サーバーごとに、実行のたびに変わる origin を割り当てる。
記事のコードには、redirect_uri のような URL を固定の値で書かない。
実行環境が、待ち受けたポートごとの URL をファイルに書き、訪問者のコードはそのファイルを読む。
Verification¶
同じファイル(3001 番のモックの認可サーバー、3000 番のクライアント、cookie パッケージ)を 3 つの方式に置き、プレビューの iframe で認可コードフローを流した。
端末は Apple M2、ブラウザは Playwright 1.65.0 の Chromium、WebKit、Firefox(headless)、配信は localhost である。
実機の Safari と Firefox、モバイルの端末、本番の HTTPS のドメインは未確認である。
| 項目 | WebContainers 1.6.4 | Nodebox 0.1.9 | almostnode 0.2.14 |
|---|---|---|---|
| 認可コードフロー | 最後まで通る(Chromium、WebKit) | 止まる(3 つのブラウザ) | 止まる(Chromium) |
Set-Cookie の保存と送信 |
通る | 届かない。SameSite=None; Secure も同じ |
届かない |
サーバーの間の fetch |
通る | 未確認 | 実際のネットワークへ出て、失敗 |
| 2 つのサーバーの分離 | 別のプロセス | 別のシェル | process.env と http を共有 |
| 起動(Chromium) | 1 回目 3.6 秒、2 回目 1.2 秒 | 0.15 秒 | 0.01 秒 |
npm install(cookie 1 つ) |
2.0 秒 | サーバーの待ち受けまで 1.0 秒に含む | 1.9〜4.1 秒 |
| フローの終わりまでの転送量 | 4.19 MB | 0.32 MB(iframe の中の本体は入っていない見込み。未確認) | 2.96 MB(非圧縮) |
- Firefox では、WebContainers の
boot、npm install、サーバーの待ち受けまで通ったが、プレビューの iframe にページが出なかった。 - WebContainers は、サーバーが返した Cookie をブラウザの Cookie の保存場所に入れず、次のリクエストに付けた。
SameSite、Secure、Domainの属性の扱いは未確認である。 - StackBlitz の iframe が、
stackblitz.comの Cookie を訪問者のブラウザに置いた。 localhostでは、API キーなしでWebContainer.boot()が動いた。本番の HTTPS のドメインで、API キーなしで動くかは未確認である。
Considered Options¶
| 選択肢 | 強み | 弱み |
|---|---|---|
| 採用: WebContainers を、StackBlitz に問い合わせずに採り、差し替えられる作りにする | Cookie を含む認可コードフローが、コードを変えずに通る。Node の API と npm が、手元の Node に近い | 利用規約の「production or commercial」と食い違う読みが残り、API を止められることがある。止められると、OIDC の記事が動かなくなる。本体を StackBlitz の CDN から読む。cross-origin isolation が要る。Firefox とモバイルで動かない見込みがある |
| almostnode に、Cookie、サーバーの間の通信、プロセスの分離を追加する | MIT で、自分のサイトから配れる。cross-origin isolation が要らない | 3 つの層を自分で作り、保守する。作れることを確認したのは Cookie だけである。訪問者のコードが、ホストのページのメインスレッドで動く |
| Nodebox を採る | cross-origin isolation が要らない。起動が速い | Set-Cookie がブラウザに届かず、認可コードフローが止まる。crypto.randomUUID が無い |
| StackBlitz に問い合わせ、返事を承認の条件にする | 規約の 2 つの読みの食い違いを、StackBlitz の答えで決められる | 返事を待つ。返事が無いことがある。「ライセンスが要る」と答えられると、使えなくなる |
Consequences¶
Positive¶
- 記事のコードを、手元の Node で動かすコードとほぼ同じ形で書ける。
- 実行環境を差し替えるときに変えるコードが、1 つのモジュールに閉じる。
- StackBlitz の返事を待たずに、実行環境を作り始められる。
Negative¶
- 利用規約の読みでは、ライセンスなしの利用になる。StackBlitz が API の利用を止めることがある。
- API を止められると、almostnode に 3 つの層を追加するまで、OIDC の記事が動かない。追加できるかは、Cookie のほかは確認していない。
- 実行環境が動くかどうかが、StackBlitz の CDN と規約に依る。
- 訪問者のブラウザに、
stackblitz.comの Cookie が置かれる。第三者の Cookie をブロックする設定では、プレビューが動かないことがある。 - Firefox とモバイルのブラウザの訪問者は、Node の実行環境を使えない見込みがある。
- ブラウザの DevTools の Application のタブで、実行環境の Cookie を見る説明を、記事に書けない。