ADR-0049: 記事の埋め込みの初期ファイルと設定を、apps/web のビルドが JSON にし、ツールのページが埋め込みの ID で読む¶
Status¶
承認
Context¶
記事から、プレイグラウンドのツールへ、初期ファイルと設定を渡す方法を決める必要がある。
- 埋め込むコンテンツは、1 本の記事にいくつでも置ける。
- 記事は、
apps/web/src/content/articles/の下の Markdown で、contentIdを持つ。articles/<series>/のディレクトリが 1 つのシリーズになり、ディレクトリの入れ子は 1 階層までである。シリーズに入れない記事は、articles/の直下に置く。- ADR-0030: 種別を記事の 1 つにし、構成を記事の形式で、シリーズをディレクトリで表す
- プレイグラウンドでは、他人が用意したコードを、訪問者の操作なしに動かさない。運営者が用意した初期ファイルは、この条件の対象にしない。
- 記事には、ツールのページを iframe で埋め込む。
apps/webのビルドは、Node の実行環境を埋め込む記事の URL を集め、_headersに書く。 - 記事に埋め込むページと、トップレベルで開くページは、同じ URL である。
fukuemon.devは、キャンバスの図のパスに、play.fukuemon.devを許すAccess-Control-Allow-Originを返す。- package を切り出すのは、2 つ以上の消費者が実在するか、消費する側と別の理由で変わるときである。
apps/playgroundは、apps/webと@fukuemon/content-modelに依存しない。
Decision¶
1. 埋め込みの ID とプロジェクト¶
- 1 つの埋め込みが持つファイルの一式と設定を、プロジェクトと呼ぶ。
- 埋め込みの ID は、記事の
contentIdと、記事の中の埋め込みの名前の組にする(<contentId>/<埋め込みの名前>)。記事の中で、埋め込みの名前は重ならない。
2. 運営者が書くファイル¶
運営者は、プロジェクトを、記事の Markdown があるディレクトリの下の playground/<埋め込みの名前>/ に置く。
シリーズの記事は articles/<series>/playground/、シリーズに入れない記事は articles/playground/ である。
apps/web/src/content/articles/playground/<埋め込みの名前>/
apps/web/src/content/articles/<series>/playground/<埋め込みの名前>/
playground.json プロジェクトの設定
... 初期ファイル
設定のファイルは、次の値を持つ。
| キー | 中身 | 使うツール |
|---|---|---|
tool |
ツールの種類。node、sql、go のどれか |
全て |
open |
最初に開くファイルのパス | 全て |
start |
起動のコマンドの一覧 | node |
database |
true のときに、実行環境が DB のプロセスを起動する |
node |
initialSql |
初期データの SQL のファイルのパス。JSON には、ファイルの中身を入れる | node、sql |
startは、コマンド、引数の一覧、プロセスの名前を持つ値の配列で書く。ツールのページが、コマンドの文字列を引数に分ける処理を持たないためである。- 設定のファイルは、訪問者のファイルのツリーに出さない。
playground/のディレクトリは、シリーズとして扱わず、ディレクトリの入れ子の数に数えない。
3. 渡す方法¶
apps/webのビルドが、プロジェクトごとに、初期ファイルと設定を 1 つの JSON にして出力する。fukuemon.devは、JSON のパスに、play.fukuemon.devを許すAccess-Control-Allow-Originを返す。- 記事の iframe の URL には、埋め込みの ID だけを書く(
https://play.fukuemon.dev/node/?project=<埋め込みの ID>)。 - ツールのページが、埋め込みの ID から JSON の URL を組み立てて、
fetchする。iframe の URL のクエリには、JSON の URL を書けない。 projectのパラメータが無いときは、ツールのページが、運営者が用意したデフォルトのプロジェクトを開く。- JSON を読めないときと、埋め込みの ID に当たるプロジェクトが無いときは、ツールのページに、エラーと、
fukuemon.dev/playgroundへのリンクを出す。デフォルトのプロジェクトは開かない。 apps/webのビルドは、設定のtoolがnodeのプロジェクトを埋め込む記事の URL を集め、_headersに書く。
URL に書けるのを埋め込みの ID だけにする理由は、他人が、自分の用意したファイルを読み込ませる URL を作れないようにするためである。
4. JSON の形の置き場¶
JSON と、設定のファイルの型と schema は、新しい package(packages/playground-project、@fukuemon/playground-project)に置く。
- package は、型と schema だけを持つ。JSON を出力する処理と、読み込む処理は持たない。
apps/webのビルドは、この schema で、設定のファイルを検証する。apps/playgroundは、読んだ JSON を、同じ schema で検証する。schema に合わないときは、JSON を読めないときと同じ表示にする。- package は、workspace のほかの package に依存しない。
package にする理由は、JSON を書く側(apps/web)と、読む側(apps/playground)の 2 つが、同じ形を使うためである。
Considered Options¶
渡す方法の選択肢は次のとおり。
| 選択肢 | 強み | 弱み |
|---|---|---|
採用: apps/web のビルドが JSON を出力し、URL には埋め込みの ID を書く |
トップレベルで開いたページも、同じ URL で同じ初期ファイルを読める。記事を直したときに、apps/web だけをデプロイする |
fukuemon.dev が、JSON のパスに CORS のヘッダを返す |
apps/playground のビルドが、記事の初期ファイルを読んで JSON を出力する |
同じ origin なので、CORS のヘッダが要らない | apps/playground のビルドが、apps/web の記事に依存する。記事を直すたびに、プレイグラウンドもデプロイする |
記事のページから postMessage で渡す |
追加のリクエストが無い | トップレベルで開いたページは、初期ファイルを受け取れない |
| iframe の URL に、JSON の URL を書く | ツールのページが、URL の組み立てを知らずに済む | 他人が、自分の用意したファイルを読み込ませる URL を作れる |
| 初期ファイルを圧縮して、URL のフラグメントに書く | リクエストが無い | URL が長くなる。他人が用意したファイルを読み込ませる URL を作れる |
設定を書く場所の選択肢は次のとおり。
| 選択肢 | 強み | 弱み |
|---|---|---|
| 採用: プロジェクトのディレクトリの、設定のファイルに書く | 設定が、初期ファイルと同じ場所にまとまる。1 つの記事に、複数の埋め込みを置ける | 設定のファイルの schema を作る |
| 記事の frontmatter に書く | 記事の設定が、1 か所にまとまる | 複数の埋め込みを置くときに、埋め込みごとに設定を分ける書き方を決めることになる |
| 記事の本文で、埋め込みのコンポーネントに渡す属性に書く | 埋め込む場所で、設定が読める | 起動のコマンドのような長い値を、属性に書く |
JSON の形の置き場の選択肢は次のとおり。
| 選択肢 | 強み | 弱み |
|---|---|---|
| 採用: 型と schema だけを持つ package を作る | 書く側と読む側が、同じ形を使う。形を変えると、typecheck が失敗する | package が 1 つ増える |
apps/playground が、@fukuemon/content-model に依存する |
package が増えない | apps/playground が、記事とシリーズの schema にも依存する |
| 2 つのアプリが、形を別々に持ち、ビルドのときに 2 つの形を比べる | package の依存が増えない | 同じ形を 2 か所に書く。比べる処理を作る |
Consequences¶
Positive¶
- 記事の iframe と、トップレベルで開いたページが、同じ URL で同じプロジェクトを開く。
- 他人が用意したファイルを、URL で読み込ませられない。
- 1 本の記事に、複数の埋め込みを置ける。
Negative¶
fukuemon.devが、プロジェクトの JSON のパスに、CORS のヘッダを返す。- ツールのページが、
fukuemon.devの JSON のパスの形を知っている。パスを変えるときは、2 つのアプリを直す。 - 設定のファイルの schema と、ビルドのときの検証を作る。
apps/playgroundが、ブラウザで schema を検証するので、ツールのページのコードに、検証のコードが入る。- package が 1 つ増える。