防御的パース
parsePanelConfig()(packages/)は、任意の JSON を、エディタが 読み込める DocState へと戻します。この関数は決して例外を投げないように書かれています。 なぜなら、その入力は、ユーザーが手編集した JSON、古いエクスポート、あるいは別のツールが 生成した JSON など何であってもよく、必ずしも正しい形の PanelConfig とは限らないからです。
Note
各フィールドは、ドキュメント全体を検証して最初の不正な値で拒否するのではなく、個別に防御されます——クランプ、デフォルト化、または破棄されます。1 つの不正なフィールドは、 インポート全体を沈めるのではなく、緩やかに劣化します。
トップレベルの入力
| Input | 結果 |
|---|---|
プレーンオブジェクトでない(null、プリミティブ、配列、関数など) | 完全に createDefaultDoc() にフォールバックする。 |
layers を欠いたプレーンオブジェクト | layers は [] になる。 |
layers はあるが配列でない | [] として扱われる。 |
| 認識されないトップレベルのキー | 黙って無視される——パース後の DocState には現れない。 |
panelHp
parseHp() は input.hp を読み取り、トップレベルの hp が無い場合は input.panel.hp に フォールバックします。値が受け入れられるのは、それが 0 より大きい有限の数値である場合だけです。 それ以外——欠落、非数値、NaN、Infinity、文字列、ゼロ、負の値——は、意味をなさない パネル寸法をそのまま伝播させるのではなく、DEFAULT_PANEL_HP(新規ドキュメントで使われるのと 同じデフォルト)にフォールバックします。
hp と食い違う、手編集された panel.widthMm / panel.heightMm は、インポート時に 無視されます——これらのフィールドは参考用の出力にすぎず(PanelConfig フォーマット を参照)、ドキュメントが再シリアライズされるときには常に hp から再計算されます。
レイヤー
layers 配列の各エントリは、parseLayer() によって独立してパースされます。
オブジェクトでないエントリ(
null、数値、文字列など)は、配列内から破棄されます。認識されない
type——shape/pattern/path/text/image以外の値——は、 推測されるのではなく破棄されます。id——空でない文字列であれば保持され、そうでなければmintId('layer')で新しい id が 生成されます。name——文字列であれば保持され、そうでなければ""がデフォルトになります。hidden——真偽値の場合のみ保持され、そうでなければパース後のレイヤーからこのフィールドは 完全に省略されます(falseに設定されるのではありません)。レイヤーごとの余分・未知のフィールドは破棄されます——パース後のレイヤーオブジェクトには 残りません。
レイヤータイプ別のフィールドごとのフォールバック
| Layer type | Field | 不正入力時の挙動 |
|---|---|---|
| all | color / fill / stroke | ちょうど 0、1、2 以外の値は 0(黒)にクランプされる。 |
すべての数値フィールド(x、y、width、height、sizeMm、strokeWidth など) | — | 数値でない、または有限でない値は 0 がデフォルトになる。 |
| all | rotation | 有限の数値の場合のみ保持され、そうでなければフィールドは省略される(デフォルトの回転は書き込まれない)。 |
shape | shape | "ellipse" 以外はすべて "rect" がデフォルトになる。 |
pattern | patternType | 認識できなくてもそのまま保持される——core は、それを検証するパターンレジストリに依存していない(ラウンドトリップ を参照)。文字列でない値は "unknown" がデフォルトになる。 |
pattern | params | プレーンオブジェクトでなければ → {}。そうでなければ、キーごとに数値かつ有限の値のみが残り、数値でないエントリ(ネストしたオブジェクトを含む)は破棄される。 |
path | points | 配列でなければ → []。プレーンオブジェクトでない各点は、(パス全体を失敗させるのではなく)配列から取り除かれる。 |
path | points[].hin / hout | x と y の両方が有限の数値である場合のみ保持され、そうでなければハンドルは省略される。 |
path | extraSubpaths | 入力が配列である場合のみ設定される(各サブパスは points と同じ方法でパースされる)。存在・非存在——空配列であっても——は、正規化して消し去られるのではなく正確に保持されるため、ラウンドトリップは正確なまま保たれる。 |
path | closed | 入力がちょうど true の場合のみ true。それ以外はすべて false になる。 |
path | fill / stroke | null は null のまま。それ以外はすべて、上記の color のクランプを通る。 |
text | content / fontFamily | 文字列でない値は "" がデフォルトになる。 |
image | src | 文字列でない値は "" がデフォルトになる。 |
これが重要な理由
パースが決して例外を投げないため、エディタ(および @zpd/core を再利用するあらゆるもの)は、 「この JSON を読み込む」処理を全域関数として扱えます。最悪の場合でも、未処理の例外ではなく 安全なドキュメントへと劣化します。ただし、どのようにフォールバックするかは入力の形によって 異なります。
オブジェクトでない不正な入力(
null、プリミティブ、配列、関数など)は、createDefaultDoc()へ完全にフォールバックします——新規セッションが開始するのと同じドキュメントで、ドットグリッド レイヤーもそのまま含まれます。フィールドが欠落・不正なプレーンオブジェクト——空オブジェクト
{}を含む——は、フィールド ごとに修復されます。panelHpはDEFAULT_PANEL_HPになり、layersは[]になります。これは 新規セッションのドキュメントではありません。デフォルトのパネル寸法は持ちますが、レイヤー リストは空で、ドットグリッドレイヤーはありません。
これは packages/ で直接検証されています。parsePanelConfig に null、42、'not json'、true、[]、素の関数といった入力を与えて、それぞれが決して例外を 投げず createDefaultDoc() に等しいことをアサートし、さらに parsePanelConfig({}) が正の panelHp と空の layers 配列を返すことを個別に確認しています。