next2d-development-mcp 1.1.5 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { registerResources } from "./resources/index.js";
6
6
  import { registerPrompts } from "./prompts/index.js";
7
7
  const server = new McpServer({
8
8
  "name": "next2d-development-mcp",
9
- "version": "0.0.2"
9
+ "version": "1.1.6"
10
10
  });
11
11
  registerTools(server);
12
12
  registerResources(server);
@@ -1626,12 +1626,26 @@ style-src 'self';
1626
1626
  ## Anti-Patterns
1627
1627
 
1628
1628
  ```typescript
1629
- // NG: Viewでビジネスロジック
1629
+ // NG: Viewでビジネスロジック + addEventListener に async 関数を直接渡す
1630
+ // async () => {} を渡すと Promise が返り、@next2d/player がチャンネルを保持したまま
1631
+ // SPA 遷移で閉じられ "A listener indicated an asynchronous response by returning true,
1632
+ // but the message channel closed before a response was received" エラーが発生する。
1630
1633
  class BadView extends View {
1631
1634
  async initialize() {
1632
1635
  btn.addEventListener(PointerEvent.POINTER_DOWN, async () => {
1633
- const data = await Repository.get(); // NG
1634
- this.processData(data); // NG
1636
+ const data = await Repository.get(); // NG: ビジネスロジック in View
1637
+ this.processData(data); // NG: ビジネスロジック in View
1638
+ });
1639
+ }
1640
+ }
1641
+
1642
+ // OK: 同期ハンドラ内で void IIFE を使い Promise を捨てる
1643
+ class GoodView extends View {
1644
+ async initialize() {
1645
+ btn.addEventListener(PointerEvent.POINTER_DOWN, () => { // 同期関数
1646
+ void (async () => {
1647
+ await this.vm.onClickButton(); // OK: VMに委譲
1648
+ })();
1635
1649
  });
1636
1650
  }
1637
1651
  }
@@ -1730,6 +1730,108 @@ homeContentPointerDownEvent(event: PointerEvent): void
1730
1730
 
1731
1731
  View内でイベント処理を完結させず、必ずViewModelに委譲します。
1732
1732
 
1733
+ ### 4. `addEventListener` に `async` 関数を直接渡さない
1734
+
1735
+ **NG パターン(Promiseエラーの原因):**
1736
+
1737
+ ```typescript
1738
+ btn.addEventListener(PointerEvent.POINTER_DOWN, async () =>
1739
+ {
1740
+ await this.vm.onClickSomething();
1741
+ });
1742
+ ```
1743
+
1744
+ `async` 関数は Promise を返します。@next2d/player の内部イベントシステムは、
1745
+ ハンドラが truthy な値を返すと「非同期応答あり」と解釈し、Worker との
1746
+ メッセージチャンネルを保持します。SPA 遷移で `gotoView()` が呼ばれると
1747
+ チャンネルが閉じられ、以下のエラーが `Uncaught (in promise)` として発生します。
1748
+
1749
+ ```
1750
+ A listener indicated an asynchronous response by returning true,
1751
+ but the message channel closed before a response was received
1752
+ ```
1753
+
1754
+ **OK パターン(`void (async () => {})()` でラップ):**
1755
+
1756
+ ```typescript
1757
+ btn.addEventListener(PointerEvent.POINTER_DOWN, () =>
1758
+ {
1759
+ if (!btn.enabled) { return; } // 同期ガードはここで
1760
+ btn.disable();
1761
+
1762
+ void (async () =>
1763
+ {
1764
+ try {
1765
+ await this.vm.onClickSomething();
1766
+ } catch (error) {
1767
+ btn.enable();
1768
+ console.error("[prefix]", "遷移に失敗", error);
1769
+ }
1770
+ })();
1771
+ });
1772
+ ```
1773
+
1774
+ ポイント:
1775
+ - 外側のハンドラを **同期関数** にして `void` 以外の戻り値を返さない
1776
+ - `await` が必要な処理は内側の即時実行 async IIFE に閉じ込め、`void` で破棄
1777
+ - `enabled` チェックなど即時判断が必要なガードは **外側の同期ハンドラ** に置く
1778
+
1779
+ ### 5. SPA 遷移時の「メッセージチャンネルクローズ」エラーを防ぐ
1780
+
1781
+ **エラーメッセージ:**
1782
+ ```
1783
+ Uncaught (in promise) Error: A listener indicated an asynchronous response
1784
+ by returning true, but the message channel closed before a response was received
1785
+ ```
1786
+
1787
+ **発生メカニズム(2つの独立した原因):**
1788
+
1789
+ | # | 原因 | 性質 | 対処 |
1790
+ |---|------|------|------|
1791
+ | ① | `addEventListener` に `async` 関数を直接渡した(Promise を返すため) | JavaScript Promise rejection | 設計原則4の `void (async () => {})()` パターンで修正 |
1792
+ | ② | @next2d/player が `app.initialize()` 完了後に DOM 追加する IME 用 `<textarea tabindex="-1">` を Chrome 拡張機能(パスワードマネージャー等)が検出し、SPA 遷移でチャンネルが閉じる | **Chrome 拡張機能のランタイムエラー**(JS Promise rejection ではない) | 初期化後に textarea へ `autocomplete="off"` 等を設定して拡張機能の検出を抑制 |
1793
+
1794
+ > **重要:** 原因②は JavaScript の Promise rejection ではなく Chrome 拡張機能のランタイムエラーです。
1795
+ > そのため `window.addEventListener("unhandledrejection", ...)` では**捕捉・抑制できません**。
1796
+ > `unhandledrejection` ハンドラは原因①(コード起因)の残存ケースに対する安全網として追加します。
1797
+
1798
+ **アプリエントリポイント (`index.ts`) への対処:**
1799
+
1800
+ ```typescript
1801
+ // ① コード起因の Promise rejection が残存する場合の安全網(unhandledrejection で捕捉可能)
1802
+ // 原因②(Chrome 拡張起因)はここでは捕捉できないため、別途 textarea 対策が必要
1803
+ window.addEventListener("unhandledrejection", (event) =>
1804
+ {
1805
+ const reason = event.reason;
1806
+ const message = (typeof reason === "string"
1807
+ ? reason
1808
+ : (reason as { message?: string } | null)?.message) ?? "";
1809
+ if (message.includes("A listener indicated an asynchronous response by returning true")) {
1810
+ event.preventDefault();
1811
+ }
1812
+ });
1813
+
1814
+ const boot = async (): Promise<void> =>
1815
+ {
1816
+ await app.initialize(config, packages).run();
1817
+
1818
+ // ② @next2d/player の IME 用グローバル textarea に Chrome 拡張が干渉するのを防ぐ
1819
+ // tabIndex="-1" の textarea は @next2d/player が app.initialize() 完了時に DOM 追加する
1820
+ // Chrome の自動補完やパスワードマネージャーがこの textarea を検出すると
1821
+ // SPA 遷移時にメッセージチャンネルエラーが発生する(JS Promise rejection ではなく
1822
+ // Chrome 拡張のランタイムエラーのため unhandledrejection ハンドラでは捕捉不可)
1823
+ const internalTextarea = document.querySelector('textarea[tabindex="-1"]');
1824
+ if (internalTextarea) {
1825
+ internalTextarea.setAttribute("autocomplete", "off");
1826
+ internalTextarea.setAttribute("data-form-type", "other");
1827
+ internalTextarea.setAttribute("role", "presentation");
1828
+ internalTextarea.setAttribute("aria-hidden", "true");
1829
+ }
1830
+
1831
+ await app.gotoView();
1832
+ };
1833
+ ```
1834
+
1733
1835
  ## View/ViewModel作成のテンプレート
1734
1836
 
1735
1837
  ### View
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "next2d-development-mcp",
3
3
  "displayName": "Next2D Development MCP",
4
- "version": "1.1.5",
4
+ "version": "1.1.6",
5
5
  "description": "MCP server for Next2D application development assistance",
6
6
  "type": "module",
7
7
  "author": "Toshiyuki Ienaga <ienaga@next2d.app>",
@@ -52,19 +52,19 @@
52
52
  },
53
53
  "devDependencies": {
54
54
  "@eslint/js": "^10.0.1",
55
- "@types/node": "^25.6.0",
55
+ "@types/node": "^25.6.2",
56
56
  "@types/vscode": "^1.115.0",
57
- "@typescript-eslint/eslint-plugin": "^8.58.2",
58
- "@typescript-eslint/parser": "^8.58.2",
59
- "@vscode/vsce": "^3.8.0",
60
- "eslint": "^10.2.0",
57
+ "@typescript-eslint/eslint-plugin": "^8.59.2",
58
+ "@typescript-eslint/parser": "^8.59.2",
59
+ "@vscode/vsce": "^3.9.1",
60
+ "eslint": "^10.3.0",
61
61
  "eslint-plugin-unused-imports": "^4.4.1",
62
- "globals": "^17.5.0",
63
- "typescript": "^6.0.2",
64
- "vitest": "^4.1.4"
62
+ "globals": "^17.6.0",
63
+ "typescript": "^6.0.3",
64
+ "vitest": "^4.1.5"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">=22.0.0",
68
- "vscode": "^1.110.0"
68
+ "vscode": "^1.115.0"
69
69
  }
70
70
  }