インストール
npm install -D @tuvix.js/devtools
# or
pnpm add -D @tuvix.js/devtools使いどころ
オーケストレーターが把握しているマイクロアプリ、そのうち現在マウントされているもの、バスを流れるイベントを確認したいときに、開発環境でインストールします。オーバーレイはページの右下隅に表示され、1秒ごとに更新されます。
コンポーネントツリー、ネットワークのタイミング、イベントのペイロードをUIで確認したい場合は、ブラウザのDevToolsやフレームワークのdevtools拡張機能を使ってください。このパッケージが表示するのは、アプリ名、1つのルート文字列、タイムスタンプ付きのイベント名です。ペイロードはロガーに保持され、コンソールから読み取れます。
クイックスタート
import { createOrchestrator } from '@tuvix.js/core';
const orchestrator = createOrchestrator({
router: {
mode: 'history',
routes: [
{ path: '/dashboard/*', app: 'dashboard' },
{ path: '/settings/*', app: 'settings' },
],
},
});
// Install before register() so the log records tuvix:app:registered.
if (process.env.NODE_ENV !== 'production') {
const { installDevTools } = await import('@tuvix.js/devtools');
installDevTools(orchestrator);
}
orchestrator.register({ name: 'dashboard', entry: '/apps/dashboard.js', container: '#main' });
orchestrator.register({ name: 'settings', entry: '/apps/settings.js', container: '#main' });
await orchestrator.start();/dashboardを開くと、オーバーレイにはdashboardが緑の点で、settingsが白の点で表示されます。Route行には/dashboardと表示され、Eventsリストにはdashboardのマウントに伴うtuvix:app:status-changeとtuvix:app:mountのイベントが表示されます。
パネルに表示される内容
installDevToolsはdocument.bodyに<div id="tuvix-devtools">を1つ追加します。この要素には、丸いトグルボタン(右下隅から12pxの位置に固定、z-index: 99999)と、その上に幅280pxのパネルが含まれます。パネルには3つのセクションがあります。
| セクション | ソース | 内容 |
|---|---|---|
| Apps | orchestrator.getRegisteredApps()とgetMountedApps() | 登録済みアプリごとに1行。緑の点はステータスがmountedであることを示します。白の点は、loadingやerrorを含むそれ以外のすべてのステータスを示します。 |
| Route | orchestrator.getRouter()?.currentPath | ルーターの現在のパス。baseプレフィックスを含まず、クエリ文字列を含みます。オーケストレーターにルーターがない場合は/を表示します。 |
| Events | orchestrator.getEventBus()にアタッチされたEventLogger | 最新の10件のイベントを新しい順に表示します。各行にはローカル時刻、ダッシュ、イベント名が表示されます。 |
デフォルトではオーケストレーターはgetGlobalBus()を使うため、Eventsリストにはオーケストレーター自身のイベントに加えて、マイクロアプリが共有バスに発行したイベントも表示されます。
| イベント | 発行されるタイミング |
|---|---|
tuvix:app:registered | register()がアプリを追加したとき |
tuvix:app:bootstrap, tuvix:app:mount, tuvix:app:unmount | アプリがそのライフサイクルのステップを通過したとき |
tuvix:app:error | アプリの読み込み、ブートストラップ、マウントのいずれかが失敗したとき |
tuvix:app:status-change | アプリのステータスが変わったとき。1回のマウントで複数回発生します。 |
tuvix:route:change | ルーターがナビゲーションしたとき |
tuvix:started, tuvix:destroyed | start()とdestroy()が完了したとき |
パネルはwindow.__TUVIX_DEVTOOLS__ = { panel, logger, orchestrator }も公開します。パネルに表示されないペイロードを読むには、ブラウザのコンソールで__TUVIX_DEVTOOLS__.logger.getEntries()を実行してください。
API
installDevTools
import type { DevToolsOptions } from '@tuvix.js/devtools';
import type { IEventBus } from '@tuvix.js/core';
declare function installDevTools(
orchestrator: {
getRegisteredApps(): string[];
getMountedApps(): string[];
getEventBus(): IEventBus;
getRouter(): { currentPath: string } | null;
},
options?: DevToolsOptions
): () => void;最初の引数には、これら4つのメソッドを持つ任意のオブジェクトを渡せます。@tuvix.js/coreまたはtuvix.jsのOrchestratorが該当します。
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
maxLogEntries | number | 200 | ロガーが保持するイベントの最大数。パネルが表示するのはそのうち10件で、残りは__TUVIX_DEVTOOLS__.loggerから読み取れます。 |
autoOpen | boolean | true | falseの場合、パネルを非表示の状態でマウントします。トグルボタンは表示されたままで、それを押すとパネルが開きます。 |
updateInterval | number | 1000 | パネルを更新する間隔(ミリ秒)。 |
呼び出しのたびに、次の処理を行います。
DevToolsPanelとEventLogger(maxLogEntries)を作成します。onAnyでロガーをorchestrator.getEventBus()にアタッチします。- パネルをマウントし、
autoOpenがfalseの場合は非表示にします。 - オーケストレーターを読み取って
panel.update()を呼び出すsetIntervalを開始します。 window.__TUVIX_DEVTOOLS__を設定します。
インターバルをクリアし、ロガーをデタッチし、パネルを削除し、window.__TUVIX_DEVTOOLS__を削除するクリーンアップ関数を返します。
import { createOrchestrator } from '@tuvix.js/core';
import { installDevTools } from '@tuvix.js/devtools';
const orchestrator = createOrchestrator();
const removeDevTools = installDevTools(orchestrator, {
autoOpen: false,
updateInterval: 250,
maxLogEntries: 500,
});
// Later, for example in a hot-module-replacement dispose hook:
removeDevTools();
await orchestrator.destroy();EventLogger
class EventLogger {
constructor(maxEntries?: number); // default 200
attach(bus: IEventBus): void;
detach(): void;
getEntries(): ReadonlyArray<{ timestamp: number; event: string; data: unknown }>;
getRecent(count?: number): ReadonlyArray<{ timestamp: number; event: string; data: unknown }>; // default 20
clear(): void;
}EventLoggerはbus.onAnyを通じて、バス上のすべてのイベントを記録します。各エントリーはtimestamp(emit時点のDate.now())、event(イベント名)、data(emitされたままのペイロード。クローンはされません)を持ちます。ログがmaxEntriesを超えると、ロガーは最も古いエントリーを破棄します。
| メソッド | 動作 |
|---|---|
attach(bus) | busのすべてのイベントを購読します。バスが破棄済みの場合は例外を投げます。 |
detach() | 購読を解除します。記録済みのエントリーは残ります。 |
getEntries() | ログ全体を古い順に返します。 |
getRecent(count) | 最後のcount件のエントリーを古い順に返します。 |
clear() | ログを空にし、購読は維持します。 |
パネルなしで使い、テストでバスのトラフィックを検証したり、ページのイベントをダンプしたりできます。
import { createEventBus } from '@tuvix.js/core';
import { EventLogger } from '@tuvix.js/devtools';
const bus = createEventBus();
const logger = new EventLogger(50);
logger.attach(bus);
bus.emit('cart:add', { sku: 'A-1' });
console.log(logger.getRecent(1)); // [{ timestamp: 1761..., event: 'cart:add', data: { sku: 'A-1' } }]
logger.detach();DevToolsPanel
class DevToolsPanel {
constructor();
mount(): void;
unmount(): void;
toggle(): void;
update(state: {
registeredApps: string[];
mountedApps: string[];
currentRoute: string;
events: Array<{ timestamp: number; event: string }>;
}): void;
}DevToolsPanelはオーバーレイのDOM部分で、ポーリングは行いません。表示したい状態を渡してupdate()を呼び出します。
| メソッド | 動作 |
|---|---|
mount() | #tuvix-devtoolsを開いた状態でdocument.bodyに追加します。同じインスタンスで2回目に呼び出しても何もしません。 |
unmount() | 要素を削除します。 |
toggle() | パネル本体の表示と非表示を切り替えます。トグルボタンは残ります。mount()の前は何もしません。 |
update(state) | Apps、Route、Eventsの各セクションを再構築します。eventsの最後の10件を新しい順に表示します。mount()の前は何もしません。 |
import { DevToolsPanel } from '@tuvix.js/devtools';
const panel = new DevToolsPanel();
panel.mount();
panel.update({
registeredApps: ['header', 'cart'],
mountedApps: ['header'],
currentRoute: '/checkout',
events: [{ timestamp: Date.now(), event: 'cart:open' }],
});型
| 名前 | 説明 |
|---|---|
DevToolsOptions | installDevToolsのオプション:maxLogEntries、autoOpen、updateInterval。 |
オーケストレーターの形とログエントリーの形はエクスポートされていません。型として必要な場合は、Parameters<typeof installDevTools>[0]とReturnType<EventLogger['getEntries']>[number]を使ってください。
本番ビルド
パッケージは"sideEffects": falseを宣言していますが、コードから呼び出す静的なimport { installDevTools } from '@tuvix.js/devtools'はバンドルに含まれます。クイックスタートのように、ビルド時のフラグで条件分岐し、動的なimport()で読み込んでください。バンドラーはprocess.env.NODE_ENVを文字列に置き換え、到達しない分岐とそのチャンクを取り除きます。Viteアプリでは代わりにimport.meta.env.DEVで判定できます。
本番環境でオーバーレイを有効のままにすると、3つのDOMセクションを毎秒再構築するsetInterval、すべてのバスイベントの購読、そしてページ上の任意のスクリプトにオーケストレーターを渡してしまうグローバル変数というコストがかかります。
エッジケース
register()とstart()の前にインストールしてください。ロガーが受け取るのはattachの後に発行されたイベントだけで、パネルは最初の更新(呼び出しからupdateIntervalミリ秒後)まで空のままです。installDevToolsにはdocumentとwindowが必要です。サーバーレンダリング中に呼び出すとReferenceErrorを投げます。クライアント専用のコードから呼び出してください。connectRouter()を使いrouterオプションを指定しない場合、getRouter()はnullを返し、Route行には/が表示されます。getMountedApps()が数えるのはmountedステータスのみです。mountingやerrorのアプリには白の点が付き、パネルがステータス名を表示することはありません。- プライベートなバス(
createOrchestrator({ eventBus: {...} }))はorchestrator.destroy()で破棄されます。その後にインストールするとonAnyから例外が投げられます。デフォルトのグローバルバスでは、クリーンアップ関数を呼び出すまで、ロガーはdestroy()の後も記録を続けます。 - 呼び出すたびに新しいパネルが作成されます。2回呼び出すと同じ要素IDを持つ2つのオーバーレイがページに配置され、2回目の呼び出しが
window.__TUVIX_DEVTOOLS__を上書きします。その後、1回目のクリーンアップ関数は2回目の呼び出しが設定したグローバルを削除します。 - 1つのインスタンスで
EventLogger.attach()を2回呼び出すと、保存された購読解除関数が置き換えられます。その後のdetach()は2つ目の購読しか解除しません。 - ログが
maxLogEntriesに達すると、ロガーは新しいイベントごとに配列をコピーします。イベントの多いバスを持つページでは、maxLogEntriesを数百程度に抑えてください。 - パネルはアプリ名、ルート、イベント名を
textContentで書き込むため、イベント名に含まれるマークアップはテキストとして表示されます。
対象外のこと
- キーボードショートカットはなく、位置、サイズ、テーマのオプションもありません。オーバーレイは常にインラインスタイルで右下に表示されます。
- イベントのペイロード、エントリーURL、ローダーのキャッシュ状態、マウントの所要時間はUIに表示されません。ペイロードは
__TUVIX_DEVTOOLS__.logger.getEntries()から読み取ってください。 - ブラウザ拡張機能はなく、React、Vue、Svelte、Angularのdevtoolsとの連携もありません。
- タイムトラベルやイベントの再送はありません。ロガーは記録するだけで、再発行はしません。
- 本番ビルドで自動的に取り除かれることはありません。インポートの条件分岐は自分で行ってください。