Murasaki
コアコンセプト

プロセスモデル

起動、開発時の再読み込み、安全な終了処理、アプリが所有するリソースの管理。

Murasaki は、ネイティブ側と Node.js 側のイベントループを分離します。パッケージ済みアプリでは、Rust ランチャーが同梱した Node.js 子プロセスを起動・監視します。Node.js が Node Main のライフサイクルとローカル HTTP サーバーを開始した後に、ホストがレンダラーを読み込みます。

起動
  → Rustホストがユーザー単位のアプリロックを取得
  → アプリ内で使うoriginと実行時トークンを生成
  → Node.js子プロセスがsrc/main.tsを読み込む
  → main.ready(context)が完了
  → ローカルサーバーが待ち受けを開始
  → コールドスタート時の登録済みURL/ファイルをmain.openRequested()へ渡す
  → 宣言済みWebViewが各レンダラールートを読み込む

ready() が失敗すると、初期化途中のバックエンドへ UI を接続せず、アプリの起動も失敗します。データベースを開く、マイグレーションを実行する、アプリ専用サービスを開始するといった、UI の利用前に完了すべき処理をここへ置いてください。

最初のプロセスへ渡された上限付きの未加工引数と作業ディレクトリは、MainContext.launch から取得できます。開発時に渡るのは murasaki dev -- <アプリ用引数> の単独の -- より後ろだけで、Murasaki 自身の CLI オプションは含まれません。詳しくはNode Mainを参照してください。

パッケージ済みの macOS/Windows/Linux アプリをもう一度起動しても、別のバックエンドは開始しません。2回目のプロセスの argv と作業ディレクトリは main.secondInstance() へ渡され、ホストがメインウィンドウを前面に表示します。開発モードでは、現在この単一起動ロックを利用できません。

URL・ファイルを指定した起動

パッケージ済みの macOS/Windows アプリと、インストール済みの Linux .deb は、murasaki.config.tsprotocolsfileAssociations を使って、独自 URL スキームとファイル拡張子を登録できます。ready() の完了後、対象となる起動要求を共通形式へ変換して main.openRequested() へ渡します。

  • activation: cold-start / second-instance / os-event
  • transport: argv / open-url / open-file
  • targets: { kind: 'url', url, scheme } または { kind: 'file', path }

2回目の起動では、secondInstance() は未加工のプロセス引数を受け取り、openRequested() には登録対象の URL とファイルだけが渡されます。OS に依存しない URL/ファイル処理は openRequested() へ、それ以外の2回目起動用引数は secondInstance() へ実装してください。

Linux の .deb は、生成した .desktop ファイルをインストールします。展開しただけの AppDir や AppImage にも同じメタデータは含まれますが、デスクトップ環境への登録は自動では行いません。

URL やファイルを指定した起動要求は、OS を経由して届く信頼できない入力です。登録設定はフックへ渡す入力を選別しますが、送信元の認証や URL・ファイル内容の安全性までは保証しません。

終了シーケンス

通常のウィンドウ終了とアプリ終了は、次の順序で処理します。

終了要求
  → beforeQuit(context)       falseを返すとキャンセル ┐
  → context.signalを中断
  → shutdown(context)                              ┘ 全体にshutdownTimeoutMsを適用
  → Node.js子プロセスが終了
  → ネイティブホストが終了、または更新を適用

beforeQuit() は、未保存の文書がある場合などに通常終了をキャンセルできます。プロセスシグナルや開発時の Node Main 再読み込みを含む強制終了ではキャンセルできません。2つのフックを合わせたデフォルトの制限時間は10秒です。shutdown() では新しい処理の受付を先に止め、その後にバッファーの書き出しとリソースの解放を行ってください。

OS の閉じるボタンと appWindow.close() はサブウィンドウを非表示にするため、windows.open() で再表示できます。main ウィンドウを閉じる操作はアプリの終了要求として上記の処理へ進みます。終了のキャンセルはアプリ全体に作用し、ウィンドウごとにキャンセルできる close フックはありません。Node Main の windows.destroy(label) は対象を破棄し、windows.create(label) で宣言済みの設定から再作成できます。

開発時の再読み込み

murasaki dev の実行中に、設定した Node Main のエントリーファイルを変更すると、次の処理を行います。

  1. 現在の shutdown({ reason: 'dev-reload' })
  2. Vite によるエントリーモジュールの無効化
  3. 新しいライフサイクルの生成
  4. 新しい ready()

Node Main がインポートするコードも Vite のモジュールグラフへ含まれますが、現在ライフサイクルを再開するのはエントリーファイル自体を変更した場合だけです。モジュールのトップレベルでタイマーやソケットを作成せず、ready() で作成して shutdown() で閉じると、再読み込み時のリソース漏れを防げます。

アプリケーションパス

MainContext.pathsappId ごとに安定した OS 標準パスを返します。

パス用途
dataデータベース、アプリが管理するユーザー文書、永続状態
cache再生成できるキャッシュ
logsアプリログ
temp処理途中の一時ファイル

実行ファイルの隣へデータを書き込まないでください。パッケージに含まれるリソースは読み取り専用の場合があり、配置も OS ごとに異なります。また、更新時にはアプリバンドル自体が置き換わります。

プロセス障害

ホストは Node.js 子プロセスを管理し、定期的に生存を確認します。パッケージ済みバックエンドが予期せず終了した場合、ランチャーは未確認の更新引き渡しを破棄し、WebView を閉じ、バックエンドのプロセスツリーを終了して、0以外の終了コードを返します。操作できない UI だけが残ることはありません。Windows では Job Object、macOS では Node.js とサイドカー専用のプロセスグループを利用します。クラッシュ後の再起動方針、ヘルスチェック用フック、複数の独立した Worker グループを監視する公開 API はまだありません。補助プロセスに明示的な再起動方針が必要な場合は、Node Main のサイドカー監視機能を使用してください。

次へ

GitHub でこのページを改善

On this page