ADR-0052: 実行環境が追加するファイルを、訪問者の作業ディレクトリの外に置き、preload を NODE_OPTIONS で渡す¶
Status¶
承認
Context¶
実行環境が追加するファイル(preload、DB のプロセスのスクリプトと依存、待ち受けたポートの URL を書くファイル)の置き場と、preload を訪問者のプロセスに追加する方法を決める必要がある。
- 実行環境は、待ち受けたポートごとの URL をファイルに書き、Node のコードはそのファイルを読む。
- HTTP の流れとコンソールを記録する preload は、実行環境が追加する。運営者が設定に書く起動のコマンドにも、訪問者が端末で打つコマンドにも、現れない。
- 訪問者の作業ディレクトリのファイルを、IndexedDB に保存する。
node_modules、.git、ビルドの出力、シークレットのファイル、シンボリックリンクなどは保存しない。 - 訪問者は、シェルを制限なしで使う。
- Node は、環境変数
NODE_OPTIONSの--requireで、プロセスの起動のときにモジュールを読み込む。 - WebContainers の API の
mountは、渡したファイルを、作業ディレクトリの中に書く。
Decision¶
1. ファイルの置き場¶
実行環境が追加するファイルは、訪問者の作業ディレクトリの外(/tmp の下の、実行環境のディレクトリ)に置く。
- 対象は、preload のファイル、DB のプロセスのスクリプトと依存、待ち受けたポートの URL を書くファイルである。
- 訪問者のファイルのツリーと、作業ディレクトリには出ない。保存の対象にも入らない。
- 待ち受けたポートの URL を書くファイルのパスは、環境変数で渡す。Node のコードは、環境変数からパスを読む。
2. preload の追加の仕方¶
実行環境は、起動のコマンドのプロセスと、シェルに、環境変数 NODE_OPTIONS を渡す。
実行環境が自分で起動するプロセス(DB のプロセス、DB の依存の npm install)には、preload を追加しない。
NODE_OPTIONSには、preload ごとに--require <絶対パス>を並べる。- 起動のコマンドを、書き換えない。
- preload は、役割ごとにファイルを分ける。使わない役割の preload は、追加しない。
- 訪問者が
NODE_OPTIONSを上書きして、preload が読み込まれなくなっても、対応しない。
NODE_OPTIONS で渡す理由は、訪問者がシェルで打ったコマンドと、npm run が起動する node にも、preload が追加されるためである。
承認の条件¶
- 待ち受けたポートの URL を書くファイルを、
/tmpの下に置いて、訪問者のコードから読めることを確認している。 - ファイルを
/tmpの下に置く手順(WebContainers の API で直接書くか、作業ディレクトリに書いてから移すか)を決めている。
Verification¶
WebContainers 1.6.4 の中の Node 24.22.2 で測った。
マシンは Apple M2、ブラウザは Playwright 1.65.0 の Chromium、WebKit、Firefox(headless)、配信は localhost である。実機のブラウザは未確認である。
preload は、Unix ドメインソケットに接続している間はプロセスを終了させないもの(ADR-0053)と、HTTP の流れを記録するもの(ADR-0054)の 2 つを使った。
短いスクリプトは、pg の Pool か Client で、クエリを 3 つ実行して end() を呼ぶ。
| 起動の仕方 | NODE_OPTIONS |
Chromium | WebKit | Firefox |
|---|---|---|---|---|
node app.js |
無し | 途中で終了 | Client が途中で終了 |
最後まで実行 |
node app.js |
--require ./<preload>(相対パス) |
最後まで実行 | 最後まで実行 | 最後まで実行 |
node app.js |
--require <絶対パス> |
最後まで実行 | 最後まで実行 | 最後まで実行 |
node app.js |
2 つの preload | 最後まで実行 | 最後まで実行 | 最後まで実行 |
npm run seed(scripts が node app.js を呼ぶ) |
2 つの preload | 最後まで実行 | 最後まで実行 | 最後まで実行 |
シェル(jsh)を起動し、入力に node app.js を書く |
無し | 途中で終了 | 最後まで実行 | 最後まで実行 |
シェル(jsh)を起動し、入力に node app.js を書く |
2 つの preload | 最後まで実行 | 最後まで実行 | 最後まで実行 |
- 2 つの preload は、
--requireの順序のどちらでも、同じ結果だった。 - HTTP のサーバー(リクエストの中で
pgのクエリを実行する)に、2 つの preload を追加し、別のプロセスからfetchした。クエリの結果が返り、HTTP の記録の行が 1 件出た。3 つのブラウザで同じだった。 - preload のファイルを
/tmpの下に置き、NODE_OPTIONS=--require /tmp/<ディレクトリ>/<preload>で渡すと、preload が動いた。作業ディレクトリのls -aには、出なかった。ls -a /tmpには、出た。 - 試作では、ファイルを作業ディレクトリに
mountしてから、node -eで/tmpへ移した。WebContainers の API で、作業ディレクトリの外に直接書けるかは、未確認である。 - 待ち受けたポートの URL を書くファイルを、
/tmpの下に置いて、訪問者のコードから読む形は、試していない。
Considered Options¶
preload のファイルの分け方の選択肢は次のとおり。
| 選択肢 | 強み | 弱み |
|---|---|---|
採用: 役割ごとにファイルを分け、--require を並べる |
ADR-0053 と ADR-0054 の preload が、別のファイルになる。使わない役割の preload を追加しない | NODE_OPTIONS の値を、プロジェクトの設定から組み立てる |
| 1 つのファイルにまとめ、環境変数で機能を切り替える | --require が 1 つで済む |
2 つの ADR の preload が、1 つのファイルに入る |
preload の追加の仕方と、ファイルの置き場の選択肢は次のとおり。
| 選択肢 | 強み | 弱み |
|---|---|---|
採用: NODE_OPTIONS で渡し、ファイルを /tmp の下に置く |
起動のコマンド、npm run、シェルで打ったコマンドのプロセスに渡る。ファイルのツリーと作業ディレクトリに出ない。保存の対象に入らない |
訪問者が NODE_OPTIONS を上書きすると、preload が読み込まれなくなる。ls /tmp には出る |
NODE_OPTIONS で渡し、ファイルを作業ディレクトリの中に置く |
/tmp へ移す処理が要らない |
ls -a に出る。保存の対象から除く処理が要る。訪問者が消せる |
起動のコマンドを、node -r <preload> ... に書き換える |
環境変数に依らない | シェルで打ったコマンドと、npm run の中の node には、preload が追加されない |
待ち受けたポートの URL を書くファイルの置き場の選択肢は次のとおり。
| 選択肢 | 強み | 弱み |
|---|---|---|
採用: /tmp の下に置き、パスを環境変数で渡す |
保存の対象に入らないので、再訪のときに、古い URL が戻らない | Node のコードが、環境変数からパスを読む 1 行を持つ |
| 作業ディレクトリの中に置き、保存の対象から除く | 訪問者が、ファイルのツリーで中身を見られる | 保存の対象から除く規則が増える。訪問者が書き換えられる |
Consequences¶
Positive¶
- 訪問者が、端末で
node、npm runのどちらを使っても、preload が追加される。 - 訪問者のファイルのツリーと、保存したファイルに、実行環境のファイルが入らない。
Negative¶
- 訪問者が、端末で
ls /tmpを実行すると、実行環境のファイルが見える。 - 訪問者が
NODE_OPTIONSを上書きすると、HTTP の記録が取れなくなる。 - Node のコードは、待ち受けたポートの URL を、環境変数が指すファイルから読む。