Murasaki
ガイド

ディープリンクとファイル関連付け

URL スキームとドキュメントタイプを登録し、すべてのオープンリクエストを Node Main で処理します。

Murasaki は、パッケージ化された macOS / Windows / Linux アプリに対して、カスタム URL スキームとドキュメントの拡張子を登録できます。コールドスタートと、起動済みのアプリへのリクエストは、どちらも同じ Node Main の openRequested() フックに届きます。

ハンドラーを設定する

アプリが扱うスキームと拡張子を murasaki.config.ts に宣言します。

murasaki.config.ts
import { defineConfig } from 'murasaki'

export default defineConfig({
  appId: 'com.example.violet',
  productName: 'Violet',

  protocols: [
    { scheme: 'violet', name: 'Violet Link' },
  ],

  fileAssociations: [
    {
      extensions: ['vnote'],
      name: 'Violet Note',
      description: 'Violet で作成したノート',
      role: 'editor',
      mimeType: 'application/x-violet-note',
    },
  ],
})

拡張子は先頭のドットなしで指定します。スキームと拡張子は小文字に正規化されます。重複した値、予約済みのブラウザ / OS スキーム(blobfilehttphttpsjavascriptmailtoms-settingstelmurasaki など)、不正なスキーム / 拡張子 / MIME タイプは、ビルド時に拒否されます。すべてのフィールドとデフォルト値は設定リファレンスを参照してください。

オープンリクエストを処理する

Node Main のエントリを作成し、openRequested(context, event) を実装します。プラットフォームやトランスポートではなく、各ターゲットの kind で分岐してください。

src/main.ts
import { defineMain } from 'murasaki/main'

export default defineMain({
  async ready({ paths }) {
    // 最初に DB や service を初期化する。
    console.log('data directory:', paths.data)
  },

  async openRequested(_context, event) {
    for (const target of event.targets) {
      if (target.kind === 'url') {
        const url = new URL(target.url)
        if (url.hostname !== 'open') continue

        // 実行前に route を検証し、認可する。
        console.log('open link:', url.pathname)
      } else {
        // file を読む前に所有範囲、size、内容を検証する。
        console.log('open document:', target.path)
      }
    }
  },
})

Murasaki は ready() の完了後に openRequested() を呼びます。DB などの長寿命リソースを一度初期化しておけば、コールドスタート時と実行中のアプリへのリクエストを、同じハンドラーで処理できます。

イベントリファレンス

type OpenTarget =
  | { kind: 'url'; url: string; scheme: string }
  | { kind: 'file'; path: string }

interface OpenRequestEvent {
  activation: 'cold-start' | 'second-instance' | 'os-event'
  transport: 'argv' | 'open-url' | 'open-file'
  targets: OpenTarget[]
  cwd?: string
}
フィールド意味
activation最初の起動に伴うリクエスト、2つ目のプロセスから転送されたリクエスト、ネイティブの OS イベントのいずれか。
transportネイティブの配信メカニズム。診断用であり、通常のアプリケーションロジックは target.kind を使います。
targets設定済みのスキーム / 拡張子と一致した URL / ファイルのみ。1 つのイベントに複数のファイルが含まれる場合があります。
cwdargv ベースの起動における作業ディレクトリ。ネイティブの OS イベントの場合は含まれません。

2 つ目の実行ファイルの起動では、secondInstance() に生の argv / cwd が届き、openRequested() には認識済みの URL / ファイルターゲットが届きます。同じオープン処理を両方のフックで実行しないでください。一般的なアクティベーションの挙動は secondInstance() に、リンク / ファイルの意味的な処理は openRequested() に置いてください。

パッケージングの動作

アーティファクト登録と配信
macOS .appmurasaki bundle が URL / ドキュメントのメタデータをアプリの Info.plist に書き込みます。Launch Services は、起動済みのアプリを含めて URL / ファイルのオープンを配送します。.dmg はこの .app を収録しています。最終テスト前に Applications へ移動してください。
Windows NSIS .exeインストーラーが installer.windows.installMode に従い、ユーザー単位 / マシン単位のプロトコル / ファイル関連付けを登録します。起動された URL / ファイルは argv から正規化されます。
Windows MSIMSI がマシン単位のプロトコル / ファイル関連付けを登録します。起動された URL / ファイルは argv から正規化されます。
Windows ポータブルアーカイブレジストリエントリを作成しません。protocols / fileAssociations を設定しても、OS のハンドラーにはなりません。
Linux AppDir / .AppImage / .debmurasaki bundlex-scheme-handler/<scheme>application/x-<extension>MimeType= エントリを .desktop ファイルに書き込みます。.debusr/share/applications/ にインストールし、インストール / 削除時にデスクトップデータベースを更新します(update-desktop-database)。手動展開した AppDir/.AppImage には同等の OS レベルの登録手順はありませんが、直接起動した場合でも、コールドスタート時の argv(.desktopExec= 行が展開する %U/%F)と、2 回目起動時のアクティベーションはどちらも機能します。
murasaki devシステムのハンドラーメタデータをインストールしません。OS との統合は、インストール済み / パッケージ化済みのアーティファクトでテストしてください。

Windows の登録は、アプリを利用可能なハンドラーに追加しますが、ユーザーの現在のデフォルトアプリを勝手に変更することはありません。リリース間で appId を維持し、インストーラーの所有権と関連付け識別子を安定させてください。

パッケージ済みハンドラーをテストする

インストーラーを実行するか、パッケージ化されたアプリを Applications へ移動したあと、コールドスタートと起動済みのアプリの両方をテストします。

macOS
open 'violet://open/note-42'
open -a Violet ./example.vnote
Windows PowerShell
Start-Process 'violet://open/note-42'
Start-Process '.\example.vnote'

クリーンな環境で、NSIS / MSI の実際のアンインストール / アップグレードの経路もテストしてください。実行ファイルへ引数を直接渡す方法は、ハンドラーのコードを確認するには使えますが、OS への登録が正しくインストールされたことの確認にはなりません。

セキュリティ

URL、ファイルパス、起動引数は、すべて信頼できない入力です。任意のローカルプロセスが、偽の URL / パスで実行ファイルを起動できます。カスタムスキームは、送信元を証明するものではありません。

  • 期待する URL のホスト、パス、アクション、パラメーターの形を許可リストで検証する
  • ベアラートークンや長期的な認証情報をディープリンクの URL に含めない。サインインのコールバックには、ワンタイムコード、state、PKCE を使う
  • クエリ文字列、フラグメント、機密性の高いパスは、ログ / クラッシュレポートから除外する
  • ファイルをパースする前に、サイズ、パーミッション、フォーマット、内容を確認する。期待した拡張子だけでは安全性を証明できない
  • シンボリックリンクや、検証から使用までの間にファイルが変化する可能性を考慮する
  • 処理は Node Main に置き、検証済みの最小限のデータだけをレンダラーへ送る

次へ

GitHub でこのページを改善

On this page