shadow-claw 1.54.0 → 1.55.0
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/.well-known/mcp/server-card.json +1 -1
- package/.well-known/mcp.json +1 -1
- package/README.md +1 -1
- package/dist/cli/build/build.js +1 -1
- package/dist/cli/{chunk-DO2t9bUI.js → chunk--IuVyWfT.js} +24 -23
- package/dist/cli/{chunk-2ZYd9gYC.js → chunk--JpdI-Ht.js} +1 -1
- package/dist/cli/{chunk-m4Ineemy.js → chunk-4O8yrQuN.js} +1 -1
- package/dist/cli/{chunk-BiH9IXnQ.js → chunk-7S9Ywlfy.js} +1 -1
- package/dist/cli/{chunk-BmMOdYvk.js → chunk-B0SqqDSK.js} +1 -1
- package/dist/cli/chunk-BNaWaEm-.js +1 -0
- package/dist/cli/chunk-BXCsxO9e.js +1 -0
- package/dist/cli/chunk-Bq37qDoZ.js +18 -0
- package/dist/cli/{chunk-C1htf-nO.js → chunk-BrEeCKIo.js} +2 -2
- package/dist/cli/{chunk-BI1SOTM1.js → chunk-Bu3ylrhT.js} +8 -8
- package/dist/cli/{chunk-3smiGQoQ.js → chunk-C1v_s0Kb.js} +1 -1
- package/dist/cli/{chunk-DGeysxz3.js → chunk-C44G533y.js} +1 -1
- package/dist/cli/{chunk-9KiBalKu.js → chunk-C7hJLmsL.js} +1 -1
- package/dist/cli/{chunk-BFv9XSNd.js → chunk-CAaQWd6t.js} +1 -1
- package/dist/cli/chunk-CIkgn5Fh.js +1 -0
- package/dist/cli/chunk-CWEIN2kc.js +1 -0
- package/dist/cli/{chunk-BDeDZ-0L.js → chunk-CYg1lO1a.js} +1 -1
- package/dist/cli/{chunk-DXLmEQ3i.js → chunk-Ce09lwXT.js} +1 -1
- package/dist/cli/{chunk-XsfHeN4m.js → chunk-CpdqRSO-.js} +1 -1
- package/dist/cli/chunk-DB4x5L-B.js +1 -0
- package/dist/cli/{chunk-DaNyMqrk.js → chunk-DBvObsBQ.js} +1 -1
- package/dist/cli/chunk-DGPpt7Ka.js +1 -0
- package/dist/cli/chunk-DGagHluH.js +1 -0
- package/dist/cli/chunk-DUIs23D9.js +1 -0
- package/dist/cli/{chunk-CyvfQV16.js → chunk-DaurYOqg.js} +1 -1
- package/dist/cli/{chunk-DSFrcSyA.js → chunk-DfwRL0Ue.js} +1 -1
- package/dist/cli/{chunk-M2tZrzY5.js → chunk-DudYQk0p.js} +1 -1
- package/dist/cli/chunk-DvKo50Tp.js +2 -0
- package/dist/cli/{chunk-H_Mw5ypp.js → chunk-Dvrqte5Q.js} +2 -2
- package/dist/cli/{chunk-BtNO-m9T.js → chunk-DzhSExU0.js} +1 -1
- package/dist/cli/{chunk-IOMO88kZ.js → chunk-ENKlt3i9.js} +1 -1
- package/dist/cli/{chunk-Bspgzwsm.js → chunk-FWRwAX75.js} +8 -8
- package/dist/cli/chunk-Lf-Z-h48.js +32 -0
- package/dist/cli/{chunk-BucXnZ4Z.js → chunk-VRfkSLrD.js} +1 -1
- package/dist/cli/chunk-uaTxD024.js +1 -0
- package/dist/cli/cli.js +1 -1
- package/dist/cli/prerender/dsd-shell/prerender-dsd-shell.js +1 -1
- package/dist/headless-agent.js +2 -2
- package/dist/lib/components/index.js +25 -25
- package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/settings/shadow-claw-git.js +1 -1
- package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
- package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
- package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
- package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
- package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw/utils/get-route.d.ts +3 -0
- package/dist/lib/components/shadow-claw/utils/sync-slotted-sidebar-active-links.d.ts +1 -1
- package/dist/lib/components/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/shadow-claw-channels.js +1 -1
- package/dist/lib/components/shadow-claw-chat.js +1 -1
- package/dist/lib/components/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/shadow-claw-conversations.js +1 -1
- package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
- package/dist/lib/components/shadow-claw-files.js +1 -1
- package/dist/lib/components/shadow-claw-git.js +1 -1
- package/dist/lib/components/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/shadow-claw-llm.js +1 -1
- package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/shadow-claw-networking.js +1 -1
- package/dist/lib/components/shadow-claw-notifications.js +1 -1
- package/dist/lib/components/shadow-claw-page-header.js +1 -1
- package/dist/lib/components/shadow-claw-pages.js +1 -1
- package/dist/lib/components/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/shadow-claw-settings.js +1 -1
- package/dist/lib/components/shadow-claw-storage.js +1 -1
- package/dist/lib/components/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/shadow-claw-tasks.js +1 -1
- package/dist/lib/components/shadow-claw-terminal.js +1 -1
- package/dist/lib/components/shadow-claw-tools.js +1 -1
- package/dist/lib/components/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw.js +1 -1
- package/dist/lib/{configure-peer-js-D5jDCtLx.js → configure-peer-js-C9554Slv.js} +2 -2
- package/dist/lib/{configure-peer-js-D5jDCtLx.js.map → configure-peer-js-C9554Slv.js.map} +1 -1
- package/dist/lib/{file-viewer-B4CL4e0i.js → file-viewer-DBc5dxNy.js} +2 -2
- package/dist/lib/{file-viewer-B4CL4e0i.js.map → file-viewer-DBc5dxNy.js.map} +1 -1
- package/dist/lib/index.js +28 -27
- package/dist/lib/{init-control-plane-l2uFnhGV.js → init-control-plane-D-41Fm5q.js} +12 -12
- package/dist/lib/{init-control-plane-l2uFnhGV.js.map → init-control-plane-D-41Fm5q.js.map} +1 -1
- package/dist/lib/{markdown-0_p_zfxL.js → markdown-KnDWzWJJ.js} +2 -2
- package/dist/lib/{markdown-0_p_zfxL.js.map → markdown-KnDWzWJJ.js.map} +1 -1
- package/dist/lib/{orchestrator-D6wm8Sec.js → orchestrator-BEZcqgR5.js} +9 -9
- package/dist/lib/{orchestrator-D6wm8Sec.js.map → orchestrator-BEZcqgR5.js.map} +1 -1
- package/dist/lib/{orchestrator-Dh-hnIpK.js → orchestrator-Cda13e5U.js} +4 -4
- package/dist/lib/{orchestrator-Dh-hnIpK.js.map → orchestrator-Cda13e5U.js.map} +1 -1
- package/dist/lib/{peerjs-BWZnsD0B.js → peerjs-DYkUTcee.js} +5 -5
- package/dist/lib/{peerjs-BWZnsD0B.js.map → peerjs-DYkUTcee.js.map} +1 -1
- package/dist/lib/{push-client-MybDfOdi.js → push-client-CQtCFDaD.js} +3 -3
- package/dist/lib/{push-client-MybDfOdi.js.map → push-client-CQtCFDaD.js.map} +1 -1
- package/dist/lib/security/custom-element-security.d.ts +8 -4
- package/dist/lib/{shadow-claw-PdOpgTxH.js → shadow-claw-DYe9u6Tp.js} +142 -33
- package/dist/lib/shadow-claw-DYe9u6Tp.js.map +1 -0
- package/dist/lib/{shadow-claw-accounts-3bR76EOb.js → shadow-claw-accounts-D2URoHys.js} +2 -2
- package/dist/lib/{shadow-claw-accounts-3bR76EOb.js.map → shadow-claw-accounts-D2URoHys.js.map} +1 -1
- package/dist/lib/{shadow-claw-channel-config-ChdZ1iB8.js → shadow-claw-channel-config-buXoz2xv.js} +3 -3
- package/dist/lib/{shadow-claw-channel-config-ChdZ1iB8.js.map → shadow-claw-channel-config-buXoz2xv.js.map} +1 -1
- package/dist/lib/{shadow-claw-channels-CK9sc7h_.js → shadow-claw-channels-5yVJ1gLk.js} +3 -3
- package/dist/lib/{shadow-claw-channels-CK9sc7h_.js.map → shadow-claw-channels-5yVJ1gLk.js.map} +1 -1
- package/dist/lib/{shadow-claw-chat-B6uuZnJ7.js → shadow-claw-chat-CVc-7ycM.js} +7 -7
- package/dist/lib/{shadow-claw-chat-B6uuZnJ7.js.map → shadow-claw-chat-CVc-7ycM.js.map} +1 -1
- package/dist/lib/{shadow-claw-control-plane-BpRcl312.js → shadow-claw-control-plane-B5vsF91B.js} +6 -6
- package/dist/lib/{shadow-claw-control-plane-BpRcl312.js.map → shadow-claw-control-plane-B5vsF91B.js.map} +1 -1
- package/dist/lib/{shadow-claw-conversations-Dc_Xk2mB.js → shadow-claw-conversations-PGK5zqn3.js} +54 -12
- package/dist/lib/{shadow-claw-conversations-Dc_Xk2mB.js.map → shadow-claw-conversations-PGK5zqn3.js.map} +1 -1
- package/dist/lib/{shadow-claw-file-viewer-C4x1PtB2.js → shadow-claw-file-viewer-DSqn0hgx.js} +6 -6
- package/dist/lib/{shadow-claw-file-viewer-C4x1PtB2.js.map → shadow-claw-file-viewer-DSqn0hgx.js.map} +1 -1
- package/dist/lib/{shadow-claw-files-DhnHbXKf.js → shadow-claw-files-vOa9ZWbr.js} +6 -6
- package/dist/lib/{shadow-claw-files-DhnHbXKf.js.map → shadow-claw-files-vOa9ZWbr.js.map} +1 -1
- package/dist/lib/{shadow-claw-git-BeQ1ElUd.js → shadow-claw-git-D54Z85Bj.js} +4 -4
- package/dist/lib/{shadow-claw-git-BeQ1ElUd.js.map → shadow-claw-git-D54Z85Bj.js.map} +1 -1
- package/dist/lib/{shadow-claw-integrations-Bbdolk3w.js → shadow-claw-integrations-BTubMH9d.js} +2 -2
- package/dist/lib/{shadow-claw-integrations-Bbdolk3w.js.map → shadow-claw-integrations-BTubMH9d.js.map} +1 -1
- package/dist/lib/{shadow-claw-llm-DPda4L3C.js → shadow-claw-llm-8koGTNYs.js} +3 -3
- package/dist/lib/{shadow-claw-llm-DPda4L3C.js.map → shadow-claw-llm-8koGTNYs.js.map} +1 -1
- package/dist/lib/{shadow-claw-mcp-remote-Dn9c30WI.js → shadow-claw-mcp-remote-C2jwBsQt.js} +2 -2
- package/dist/lib/{shadow-claw-mcp-remote-Dn9c30WI.js.map → shadow-claw-mcp-remote-C2jwBsQt.js.map} +1 -1
- package/dist/lib/{shadow-claw-networking-zqNuyn3-.js → shadow-claw-networking-BV-rna16.js} +2 -2
- package/dist/lib/{shadow-claw-networking-zqNuyn3-.js.map → shadow-claw-networking-BV-rna16.js.map} +1 -1
- package/dist/lib/{shadow-claw-notifications-DFgdo33v.js → shadow-claw-notifications-Dku7VBLk.js} +2 -2
- package/dist/lib/{shadow-claw-notifications-DFgdo33v.js.map → shadow-claw-notifications-Dku7VBLk.js.map} +1 -1
- package/dist/lib/{shadow-claw-page-header-B7LFGQRf.js → shadow-claw-page-header-VPjYWfsT.js} +2 -2
- package/dist/lib/{shadow-claw-page-header-B7LFGQRf.js.map → shadow-claw-page-header-VPjYWfsT.js.map} +1 -1
- package/dist/lib/{shadow-claw-pages-DLspFqdK.js → shadow-claw-pages-DXrM6hgZ.js} +29 -19
- package/dist/lib/shadow-claw-pages-DXrM6hgZ.js.map +1 -0
- package/dist/lib/{shadow-claw-peerjs-D8xWwuWR.js → shadow-claw-peerjs-D3xonez5.js} +3 -3
- package/dist/lib/{shadow-claw-peerjs-D8xWwuWR.js.map → shadow-claw-peerjs-D3xonez5.js.map} +1 -1
- package/dist/lib/{shadow-claw-settings-NbvzXASD.js → shadow-claw-settings-DMTx6OS9.js} +5 -5
- package/dist/lib/{shadow-claw-settings-NbvzXASD.js.map → shadow-claw-settings-DMTx6OS9.js.map} +1 -1
- package/dist/lib/{shadow-claw-storage-B_NAoZmd.js → shadow-claw-storage-BJLnukxa.js} +3 -3
- package/dist/lib/{shadow-claw-storage-B_NAoZmd.js.map → shadow-claw-storage-BJLnukxa.js.map} +1 -1
- package/dist/lib/{shadow-claw-task-server-BaQJXhYJ.js → shadow-claw-task-server-DcMj20hD.js} +2 -2
- package/dist/lib/{shadow-claw-task-server-BaQJXhYJ.js.map → shadow-claw-task-server-DcMj20hD.js.map} +1 -1
- package/dist/lib/{shadow-claw-tasks-CAZua0-j.js → shadow-claw-tasks-BQBe4lVi.js} +7 -7
- package/dist/lib/{shadow-claw-tasks-CAZua0-j.js.map → shadow-claw-tasks-BQBe4lVi.js.map} +1 -1
- package/dist/lib/{shadow-claw-terminal-B2YJhkJL.js → shadow-claw-terminal-DPEzTxXv.js} +2 -2
- package/dist/lib/{shadow-claw-terminal-B2YJhkJL.js.map → shadow-claw-terminal-DPEzTxXv.js.map} +1 -1
- package/dist/lib/{shadow-claw-tools-CkZWxxTm.js → shadow-claw-tools-BCnU8FaK.js} +6 -6
- package/dist/lib/{shadow-claw-tools-CkZWxxTm.js.map → shadow-claw-tools-BCnU8FaK.js.map} +1 -1
- package/dist/lib/{shadow-claw-webvm-BL9cgrP9.js → shadow-claw-webvm-CnW-hoJ8.js} +2 -2
- package/dist/lib/{shadow-claw-webvm-BL9cgrP9.js.map → shadow-claw-webvm-CnW-hoJ8.js.map} +1 -1
- package/dist/lib/subsystems/mcp/webmcp.d.ts +15 -2
- package/dist/lib/{sync-web-mcp-registration-dW2MVCj4.js → sync-web-mcp-registration-DwpGjVdo.js} +10 -4
- package/dist/lib/sync-web-mcp-registration-DwpGjVdo.js.map +1 -0
- package/dist/lib/{tools-D7whby3H.js → tools-CP6G0HF7.js} +5 -6
- package/dist/lib/tools-CP6G0HF7.js.map +1 -0
- package/dist/lib/{trusted-types-d0cdkut_.js → trusted-types-0cu5Dm6M.js} +29 -4
- package/dist/lib/{trusted-types-d0cdkut_.js.map → trusted-types-0cu5Dm6M.js.map} +1 -1
- package/dist/lib/utils/index.d.ts +1 -0
- package/dist/lib/utils/index.js +4 -2
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/lib/utils/resolve-page-script-dependencies.d.ts +19 -0
- package/dist/lib/utils/resolve-page-script-dependencies.js +98 -0
- package/dist/lib/utils/resolve-page-script-dependencies.js.map +1 -0
- package/dist/lib/utils/utils.js +1 -1
- package/dist/lib/webmcp-Bf9rT7uu.js +2001 -0
- package/dist/lib/webmcp-Bf9rT7uu.js.map +1 -0
- package/dist/public/.well-known/mcp/server-card.json +1 -1
- package/dist/public/.well-known/mcp.json +1 -1
- package/dist/public/AGENTS.md +3 -3
- package/dist/public/README.md +1 -1
- package/dist/public/agent.worker.js +2 -2
- package/dist/public/components/shadow-claw/shadow-claw.css +1 -1
- package/dist/public/components/shadow-claw/shadow-claw.html +1 -1
- package/dist/public/components/shadow-claw-conversations/shadow-claw-conversations.css +1 -1
- package/dist/public/components/shadow-claw-conversations/shadow-claw-conversations.html +1 -1
- package/dist/public/{configure-peer-js-WLRM7Qa7.js → configure-peer-js-OYl3V8sW.js} +1 -1
- package/dist/public/{custom-element-security-Cl991z-x.js → custom-element-security-BR4V40j4.js} +1 -1
- package/dist/public/docs/example/article/index.html +3 -3
- package/dist/public/docs/example/article.html +3 -3
- package/dist/public/docs/publishing/index.html +3 -3
- package/dist/public/docs/publishing.html +3 -3
- package/dist/public/docs/skill-creator/index.html +3 -3
- package/dist/public/docs/skill-creator.html +3 -3
- package/dist/public/docs/subsystems/custom-element-security.md +19 -5
- package/dist/public/docs/subsystems/pages.md +10 -2
- package/dist/public/docs/subsystems/tools.md +1 -2
- package/dist/public/docs/subsystems/webmcp.md +41 -6
- package/dist/public/{download-group-file-fFjoIIp4.js → download-group-file-BNmK8wac.js} +1 -1
- package/dist/public/e2e/README.md +2 -2
- package/dist/public/{e2e-bridge-CiSfBRT4.js → e2e-bridge-4phD1JUQ.js} +1 -1
- package/dist/public/{file-viewer-DFC3kfeO.js → file-viewer-Dbh3K9nL.js} +1 -1
- package/dist/public/index.html +3 -3
- package/dist/public/index.js +1 -1
- package/dist/public/{init-control-plane-BEbcwmmv.js → init-control-plane-DGOY_RNS.js} +1 -1
- package/dist/public/llms.txt +1 -1
- package/dist/public/main/index.html +3 -3
- package/dist/public/main/memory/index.html +3 -3
- package/dist/public/main/memory.html +3 -3
- package/dist/public/main.html +3 -3
- package/dist/public/{markdown-BeglYzw9.js → markdown-BPZYpfGm.js} +1 -1
- package/dist/public/{orchestrator-BlvEG3PO.js → orchestrator-OoUK7CF9.js} +1 -1
- package/dist/public/{orchestrator-DTNrntd4.js → orchestrator-a9047bGu.js} +1 -1
- package/dist/public/{peerjs-CnBdK94R.js → peerjs-Cbn310Tz.js} +1 -1
- package/dist/public/{push-client-DOHbdCyj.js → push-client-Dy4LGJy9.js} +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/shadow-claw-B-HWC1uN.js +1180 -0
- package/dist/public/{shadow-claw-accounts-C6FXVw2K.js → shadow-claw-accounts-Di-anCZ1.js} +1 -1
- package/dist/public/{shadow-claw-channels-tr_r60Kq.js → shadow-claw-channels-RVwCE8pE.js} +1 -1
- package/dist/public/{shadow-claw-chat-BAAPhAOs.js → shadow-claw-chat-DI1q8UnX.js} +1 -1
- package/dist/public/{shadow-claw-control-plane-C_5VIx8E.js → shadow-claw-control-plane-KXEdLOGl.js} +2 -2
- package/dist/public/{shadow-claw-conversations-DGYZdDa3.js → shadow-claw-conversations-BCBGtS30.js} +40 -6
- package/dist/public/{shadow-claw-file-viewer-5j2-Z3fN.js → shadow-claw-file-viewer-CW3t6j-w.js} +1 -1
- package/dist/public/{shadow-claw-files-DKF4Yy0m.js → shadow-claw-files-JQ-o0ImX.js} +1 -1
- package/dist/public/{shadow-claw-git-CtUTJXKT.js → shadow-claw-git-D2M8VixW.js} +2 -2
- package/dist/public/{shadow-claw-integrations-CuBy9D3z.js → shadow-claw-integrations-Cgw6v9_1.js} +1 -1
- package/dist/public/{shadow-claw-llm-BIfNNjGL.js → shadow-claw-llm-74ieXp7i.js} +1 -1
- package/dist/public/{shadow-claw-mcp-remote-C17FXvrb.js → shadow-claw-mcp-remote-BX0BkSmy.js} +1 -1
- package/dist/public/{shadow-claw-networking-CjxQ1CLI.js → shadow-claw-networking-DNX8vaIQ.js} +1 -1
- package/dist/public/{shadow-claw-notifications-Brn2O3Ps.js → shadow-claw-notifications-BLTaGAny.js} +1 -1
- package/dist/public/{shadow-claw-page-header-Bn1P7Cxb.js → shadow-claw-page-header-CjFOdo9v.js} +1 -1
- package/dist/public/shadow-claw-pages-DOQqMm0_.js +645 -0
- package/dist/public/{shadow-claw-settings-CfDm0hVA.js → shadow-claw-settings-DFtl0Fy8.js} +2 -2
- package/dist/public/{shadow-claw-storage-DOOesRzw.js → shadow-claw-storage-gQGExZRq.js} +1 -1
- package/dist/public/{shadow-claw-task-server-H36iDDp2.js → shadow-claw-task-server-Ca45uY7S.js} +1 -1
- package/dist/public/{shadow-claw-tasks-4JsjSGkN.js → shadow-claw-tasks-CCTaaSdi.js} +1 -1
- package/dist/public/{shadow-claw-terminal-CTRTcYz-.js → shadow-claw-terminal-BfgulLkl.js} +1 -1
- package/dist/public/{shadow-claw-tools-Dm1LxNf4.js → shadow-claw-tools-V8NA4n18.js} +1 -1
- package/dist/public/{shadow-claw-webvm-JzuPe9Nr.js → shadow-claw-webvm-CxxI2bnC.js} +1 -1
- package/dist/public/sync-web-mcp-registration-CkCA8UFm.js +1 -0
- package/dist/public/{tools-FiTtOlIt.js → tools-CwQq5tJH.js} +2 -2
- package/dist/public/webmcp-CmtWOAFm.js +6 -0
- package/dist/server.js +2 -2
- package/package.json +3 -3
- package/src/cli/prerender/dsd-shell/prerender-dsd-shell.test.ts +16 -0
- package/src/cli/prerender/dsd-shell/prerender-dsd-shell.ts +62 -1
- package/src/cli/prerender/pretty-paths/prerender-pretty-paths.test.ts +277 -0
- package/src/cli/prerender/pretty-paths/prerender-pretty-paths.ts +28 -1
- package/src/cli/site-config/custom-elements/build-custom-element-script-tags.ts +13 -1
- package/src/components/shadow-claw/shadow-claw.css +12 -0
- package/src/components/shadow-claw/shadow-claw.html +12 -2
- package/src/components/shadow-claw/utils/bind-event-listeners.test.ts +18 -0
- package/src/components/shadow-claw/utils/bind-event-listeners.ts +14 -0
- package/src/components/shadow-claw/utils/get-route.test.ts +35 -0
- package/src/components/shadow-claw/utils/get-route.ts +18 -0
- package/src/components/shadow-claw/utils/sync-slotted-sidebar-active-links.test.ts +152 -1
- package/src/components/shadow-claw/utils/sync-slotted-sidebar-active-links.ts +139 -8
- package/src/components/shadow-claw-conversations/shadow-claw-conversations.css +11 -0
- package/src/components/shadow-claw-conversations/shadow-claw-conversations.html +27 -4
- package/src/components/shadow-claw-conversations/shadow-claw-conversations.ts +32 -4
- package/src/components/shadow-claw-pages/shadow-claw-pages-scripts.test.ts +266 -0
- package/src/components/shadow-claw-pages/shadow-claw-pages.ts +41 -17
- package/src/core/orchestrator/utils/sync-web-mcp-registration.test.ts +18 -1
- package/src/core/orchestrator/utils/sync-web-mcp-registration.ts +8 -0
- package/src/core/utils/init-control-plane.test.ts +9 -4
- package/src/security/custom-element-security.test.ts +65 -0
- package/src/security/custom-element-security.ts +67 -4
- package/src/stores/tools.test.ts +5 -3
- package/src/stores/tools.ts +5 -10
- package/src/subsystems/mcp/webmcp.test.ts +84 -13
- package/src/subsystems/mcp/webmcp.ts +73 -10
- package/src/utils/index.ts +1 -0
- package/src/utils/resolve-page-script-dependencies.test.ts +245 -0
- package/src/utils/resolve-page-script-dependencies.ts +198 -0
- package/dist/cli/chunk-B1hufJeO.js +0 -1
- package/dist/cli/chunk-BZpn8npn.js +0 -1
- package/dist/cli/chunk-Bw5aOTFr.js +0 -1
- package/dist/cli/chunk-C8nH01Gw.js +0 -32
- package/dist/cli/chunk-CK1ZoyoE.js +0 -17
- package/dist/cli/chunk-CVkhDr-7.js +0 -1
- package/dist/cli/chunk-Cum07Vlh.js +0 -1
- package/dist/cli/chunk-D3UhDDmV.js +0 -2
- package/dist/cli/chunk-D8GI6qj6.js +0 -1
- package/dist/cli/chunk-DEH-FvmP.js +0 -1
- package/dist/cli/chunk-DksvJ_kf.js +0 -1
- package/dist/cli/chunk-RGb9wM4R.js +0 -1
- package/dist/lib/shadow-claw-PdOpgTxH.js.map +0 -1
- package/dist/lib/shadow-claw-pages-DLspFqdK.js.map +0 -1
- package/dist/lib/sync-web-mcp-registration-dW2MVCj4.js.map +0 -1
- package/dist/lib/tools-D7whby3H.js.map +0 -1
- package/dist/lib/webmcp-CYTNZfkn.js +0 -1687
- package/dist/lib/webmcp-CYTNZfkn.js.map +0 -1
- package/dist/public/shadow-claw-CnNX-WCh.js +0 -1158
- package/dist/public/shadow-claw-pages-BwOOqRWW.js +0 -645
- package/dist/public/sync-web-mcp-registration-BnHVUjat.js +0 -1
- package/dist/public/webmcp-_TBqXlVW.js +0 -1
package/src/stores/tools.ts
CHANGED
|
@@ -79,8 +79,8 @@ export class ToolsStore {
|
|
|
79
79
|
this._enabledDeclarativeTools = new Signal.Computed(() => {
|
|
80
80
|
const tools = this._declarativeTools.get();
|
|
81
81
|
const enabledSet = this._declarativeToolNamesEnabled.get();
|
|
82
|
-
if (enabledSet ===
|
|
83
|
-
return
|
|
82
|
+
if (!enabledSet || enabledSet.size === 0) {
|
|
83
|
+
return [];
|
|
84
84
|
}
|
|
85
85
|
return tools.filter((t) => enabledSet.has(t.name));
|
|
86
86
|
});
|
|
@@ -137,8 +137,8 @@ export class ToolsStore {
|
|
|
137
137
|
|
|
138
138
|
isDeclarativeToolEnabled(name: string): boolean {
|
|
139
139
|
const enabledSet = this._declarativeToolNamesEnabled.get();
|
|
140
|
-
if (enabledSet
|
|
141
|
-
return
|
|
140
|
+
if (!enabledSet) {
|
|
141
|
+
return false;
|
|
142
142
|
}
|
|
143
143
|
return enabledSet.has(name);
|
|
144
144
|
}
|
|
@@ -183,12 +183,7 @@ export class ToolsStore {
|
|
|
183
183
|
enabled: boolean,
|
|
184
184
|
): Promise<void> {
|
|
185
185
|
const currentEnabled = this._declarativeToolNamesEnabled.get();
|
|
186
|
-
const
|
|
187
|
-
const nextSet = new Set(
|
|
188
|
-
currentEnabled === null
|
|
189
|
-
? allDeclTools.map((t) => t.name)
|
|
190
|
-
: currentEnabled,
|
|
191
|
-
);
|
|
186
|
+
const nextSet = new Set(currentEnabled === null ? [] : currentEnabled);
|
|
192
187
|
if (enabled) {
|
|
193
188
|
nextSet.add(toolName);
|
|
194
189
|
} else {
|
|
@@ -29,20 +29,24 @@ describe("webmcp integration", () => {
|
|
|
29
29
|
mockRegisterTool = jest.fn(() => undefined);
|
|
30
30
|
mockUnregisterTool = jest.fn(() => undefined);
|
|
31
31
|
|
|
32
|
-
// Mock the polyfill module so
|
|
32
|
+
// Mock the polyfill module so installWebMCP installs our
|
|
33
33
|
// test mock onto document.modelContext instead of the real polyfill.
|
|
34
|
+
const installMock = jest.fn(() => {
|
|
35
|
+
if (!(globalThis as any).document?.modelContext) {
|
|
36
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
37
|
+
configurable: true,
|
|
38
|
+
value: {
|
|
39
|
+
registerTool: mockRegisterTool,
|
|
40
|
+
unregisterTool: mockUnregisterTool,
|
|
41
|
+
addEventListener: jest.fn(),
|
|
42
|
+
removeEventListener: jest.fn(),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
34
48
|
jest.unstable_mockModule("@mcp-b/webmcp-polyfill", () => ({
|
|
35
|
-
|
|
36
|
-
if (!(globalThis as any).document?.modelContext) {
|
|
37
|
-
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
38
|
-
configurable: true,
|
|
39
|
-
value: {
|
|
40
|
-
registerTool: mockRegisterTool,
|
|
41
|
-
unregisterTool: mockUnregisterTool,
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}),
|
|
49
|
+
installWebMCP: installMock,
|
|
46
50
|
}));
|
|
47
51
|
|
|
48
52
|
jest.unstable_mockModule("../../db/open-database.js", () => ({
|
|
@@ -227,7 +231,7 @@ describe("webmcp integration", () => {
|
|
|
227
231
|
// Re-mock the polyfill to be a no-op (simulates polyfill failure)
|
|
228
232
|
jest.resetModules();
|
|
229
233
|
jest.unstable_mockModule("@mcp-b/webmcp-polyfill", () => ({
|
|
230
|
-
|
|
234
|
+
installWebMCP: jest.fn(),
|
|
231
235
|
}));
|
|
232
236
|
|
|
233
237
|
jest.unstable_mockModule("../../db/open-database.js", () => ({
|
|
@@ -1070,6 +1074,73 @@ describe("webmcp integration", () => {
|
|
|
1070
1074
|
|
|
1071
1075
|
unsubscribe();
|
|
1072
1076
|
});
|
|
1077
|
+
|
|
1078
|
+
it("subscribes to toolchange event via onWebMcpToolChange", async () => {
|
|
1079
|
+
const { onWebMcpToolChange } = await import("./webmcp.js");
|
|
1080
|
+
|
|
1081
|
+
const mockTarget = new EventTarget();
|
|
1082
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
1083
|
+
configurable: true,
|
|
1084
|
+
value: mockTarget,
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
const listener = jest.fn();
|
|
1088
|
+
const unsubscribe = onWebMcpToolChange(listener);
|
|
1089
|
+
|
|
1090
|
+
const changeEvt = new Event("toolchange");
|
|
1091
|
+
mockTarget.dispatchEvent(changeEvt);
|
|
1092
|
+
|
|
1093
|
+
expect(listener).toHaveBeenCalledWith(changeEvt);
|
|
1094
|
+
|
|
1095
|
+
unsubscribe();
|
|
1096
|
+
mockTarget.dispatchEvent(new Event("toolchange"));
|
|
1097
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
describe("annotations and discovery spec features", () => {
|
|
1102
|
+
it("supports debugging annotation in resolveWebMcpToolAnnotations", async () => {
|
|
1103
|
+
const { resolveWebMcpToolAnnotations } = await import("./webmcp.js");
|
|
1104
|
+
|
|
1105
|
+
const normal = resolveWebMcpToolAnnotations("read_file");
|
|
1106
|
+
expect(normal.debugging).toBeUndefined();
|
|
1107
|
+
|
|
1108
|
+
const debugTool = resolveWebMcpToolAnnotations("dump_state", {
|
|
1109
|
+
debugging: true,
|
|
1110
|
+
});
|
|
1111
|
+
expect(debugTool.debugging).toBe(true);
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
it("passes fromOrigins to modelContext.getTools and preserves title", async () => {
|
|
1115
|
+
const { getWebMcpTools } = await import("./webmcp.js");
|
|
1116
|
+
|
|
1117
|
+
const mockGetTools = jest.fn(async (_opts?: any) => [
|
|
1118
|
+
{
|
|
1119
|
+
name: "cross_frame_tool",
|
|
1120
|
+
title: "Cross Frame Tool",
|
|
1121
|
+
description: "A tool from another origin",
|
|
1122
|
+
inputSchema: { type: "object", properties: {} },
|
|
1123
|
+
},
|
|
1124
|
+
]);
|
|
1125
|
+
|
|
1126
|
+
Object.defineProperty((globalThis as any).document, "modelContext", {
|
|
1127
|
+
configurable: true,
|
|
1128
|
+
value: {
|
|
1129
|
+
registerTool: jest.fn(),
|
|
1130
|
+
getTools: mockGetTools,
|
|
1131
|
+
},
|
|
1132
|
+
});
|
|
1133
|
+
|
|
1134
|
+
const tools = await getWebMcpTools({
|
|
1135
|
+
fromOrigins: ["https://cart.example.com"],
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
expect(mockGetTools).toHaveBeenCalledWith({
|
|
1139
|
+
fromOrigins: ["https://cart.example.com"],
|
|
1140
|
+
});
|
|
1141
|
+
expect(tools[0].name).toBe("cross_frame_tool");
|
|
1142
|
+
expect(tools[0].title).toBe("Cross Frame Tool");
|
|
1143
|
+
});
|
|
1073
1144
|
});
|
|
1074
1145
|
});
|
|
1075
1146
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { installWebMCP } from "@mcp-b/webmcp-polyfill";
|
|
2
|
+
export type { WebMCP } from "webmcp-types";
|
|
2
3
|
|
|
3
4
|
import { DEFAULT_GROUP_ID } from "../../config/config.js";
|
|
4
5
|
import { TOOL_DEFINITIONS, type ToolDefinition } from "../tools/tools.js";
|
|
@@ -35,14 +36,37 @@ export function getWebMcpMode(): WebMcpMode {
|
|
|
35
36
|
/**
|
|
36
37
|
* Install the `@mcp-b/webmcp-polyfill` onto `document.modelContext`.
|
|
37
38
|
*
|
|
39
|
+
* In `@mcp-b/webmcp-polyfill` 6.0.0+, the entry point is `installWebMCP()`
|
|
40
|
+
* (previously `initializeWebMCPPolyfill()` in v5).
|
|
38
41
|
* The polyfill itself bails out when Chrome's native `document.modelContext`
|
|
39
42
|
* is already present — it does NOT override the native API. We call it
|
|
40
|
-
* unconditionally; if the polyfill is already installed it
|
|
41
|
-
* existing install via its own `installState` guard and return quickly.
|
|
43
|
+
* unconditionally; if the polyfill is already installed it returns quickly.
|
|
42
44
|
*/
|
|
43
45
|
function ensurePolyfill(): void {
|
|
44
46
|
try {
|
|
45
|
-
|
|
47
|
+
// Some test / mock environments or non-HTTPS dev setups may not define isSecureContext.
|
|
48
|
+
// If running in a browser-like environment without isSecureContext set, ensure safe default.
|
|
49
|
+
if (
|
|
50
|
+
typeof globalThis !== "undefined" &&
|
|
51
|
+
typeof globalThis.isSecureContext === "undefined" &&
|
|
52
|
+
typeof location !== "undefined" &&
|
|
53
|
+
(location.protocol === "https:" ||
|
|
54
|
+
location.hostname === "localhost" ||
|
|
55
|
+
location.hostname === "127.0.0.1")
|
|
56
|
+
) {
|
|
57
|
+
try {
|
|
58
|
+
Object.defineProperty(globalThis, "isSecureContext", {
|
|
59
|
+
value: true,
|
|
60
|
+
configurable: true,
|
|
61
|
+
});
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore if read-only
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof installWebMCP === "function") {
|
|
68
|
+
installWebMCP();
|
|
69
|
+
}
|
|
46
70
|
} catch (err) {
|
|
47
71
|
console.warn("WebMCP polyfill initialization failed:", err);
|
|
48
72
|
}
|
|
@@ -177,6 +201,7 @@ export interface WebMcpExecuteContext {
|
|
|
177
201
|
|
|
178
202
|
export interface WebMcpRegisteredTool {
|
|
179
203
|
name: string;
|
|
204
|
+
title?: string;
|
|
180
205
|
description?: string;
|
|
181
206
|
inputSchema?: unknown;
|
|
182
207
|
annotations?: Record<string, unknown>;
|
|
@@ -188,6 +213,7 @@ export interface WebMcpRegisteredTool {
|
|
|
188
213
|
|
|
189
214
|
export interface NormalizedWebMcpRegisteredTool {
|
|
190
215
|
name: string;
|
|
216
|
+
title?: string;
|
|
191
217
|
description?: string;
|
|
192
218
|
inputSchema: Record<string, unknown>;
|
|
193
219
|
annotations?: Record<string, unknown>;
|
|
@@ -232,14 +258,17 @@ export function extractAbortSignal(
|
|
|
232
258
|
* - Feature-detects `getTools()` on `document.modelContext` / `navigator.modelContext`.
|
|
233
259
|
* - If `getTools()` is absent (older browser builds or polyfills without getTools),
|
|
234
260
|
* returns an empty array `[]` without throwing.
|
|
261
|
+
* - Supports options dictionary with `fromOrigins` to discover cross-frame/cross-origin tools (Chrome 155+).
|
|
235
262
|
* - Normalizes `RegisteredTool#inputSchema` so caller receives a JavaScript object
|
|
236
263
|
* whether running on Chrome 154+ (native object) or Chrome < 154 (DOMString JSON).
|
|
237
264
|
*/
|
|
238
|
-
export async function getWebMcpTools(
|
|
239
|
-
|
|
240
|
-
> {
|
|
265
|
+
export async function getWebMcpTools(options?: {
|
|
266
|
+
fromOrigins?: string[];
|
|
267
|
+
}): Promise<NormalizedWebMcpRegisteredTool[]> {
|
|
241
268
|
const modelContext = getModelContextApi() as {
|
|
242
|
-
getTools?: (
|
|
269
|
+
getTools?: (options?: {
|
|
270
|
+
fromOrigins?: string[];
|
|
271
|
+
}) => Promise<WebMcpRegisteredTool[]>;
|
|
243
272
|
} | null;
|
|
244
273
|
|
|
245
274
|
if (!modelContext || typeof modelContext.getTools !== "function") {
|
|
@@ -247,13 +276,14 @@ export async function getWebMcpTools(): Promise<
|
|
|
247
276
|
}
|
|
248
277
|
|
|
249
278
|
try {
|
|
250
|
-
const rawTools = await modelContext.getTools();
|
|
279
|
+
const rawTools = await modelContext.getTools(options);
|
|
251
280
|
if (!Array.isArray(rawTools)) {
|
|
252
281
|
return [];
|
|
253
282
|
}
|
|
254
283
|
|
|
255
284
|
return rawTools.map((tool) => ({
|
|
256
285
|
name: tool.name,
|
|
286
|
+
...(tool.title ? { title: tool.title } : {}),
|
|
257
287
|
...(tool.description ? { description: tool.description } : {}),
|
|
258
288
|
inputSchema: parseWebMcpInputSchema(tool.inputSchema),
|
|
259
289
|
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
@@ -595,6 +625,34 @@ export function onWebMcpToolCancel(
|
|
|
595
625
|
return addWebMcpEventListener("toolcancel", listener, options);
|
|
596
626
|
}
|
|
597
627
|
|
|
628
|
+
/**
|
|
629
|
+
* Convenience helper to subscribe to tool registration change events (`toolchange`).
|
|
630
|
+
* Dispatched on `document.modelContext` when tools are registered or unregistered.
|
|
631
|
+
* Returns an unsubscribe cleanup function.
|
|
632
|
+
*/
|
|
633
|
+
export function onWebMcpToolChange(
|
|
634
|
+
listener: (event: Event) => void,
|
|
635
|
+
options?: boolean | AddEventListenerOptions,
|
|
636
|
+
): () => void {
|
|
637
|
+
const doc = typeof document !== "undefined" ? document : undefined;
|
|
638
|
+
const nav = typeof navigator !== "undefined" ? navigator : undefined;
|
|
639
|
+
const modelContext: any =
|
|
640
|
+
(doc as any)?.modelContext ?? (nav as any)?.modelContext;
|
|
641
|
+
|
|
642
|
+
if (!modelContext || typeof modelContext.addEventListener !== "function") {
|
|
643
|
+
return () => {};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
modelContext.addEventListener("toolchange", listener, options);
|
|
647
|
+
return () => {
|
|
648
|
+
try {
|
|
649
|
+
modelContext.removeEventListener("toolchange", listener, options);
|
|
650
|
+
} catch {
|
|
651
|
+
// Ignore cleanup error
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
598
656
|
/**
|
|
599
657
|
* Consequential tools that perform high-stakes, irreversible, or external real-world actions
|
|
600
658
|
* (such as deleting data, executing arbitrary system commands, or transmitting live communications).
|
|
@@ -649,6 +707,7 @@ export interface WebMcpToolAnnotations {
|
|
|
649
707
|
readOnlyHint?: boolean;
|
|
650
708
|
consequentialHint?: boolean;
|
|
651
709
|
untrustedContentHint?: boolean;
|
|
710
|
+
debugging?: boolean;
|
|
652
711
|
[key: string]: unknown;
|
|
653
712
|
}
|
|
654
713
|
|
|
@@ -668,6 +727,9 @@ export function resolveWebMcpToolAnnotations(
|
|
|
668
727
|
readOnlyHint: isReadOnly,
|
|
669
728
|
consequentialHint: isConsequential,
|
|
670
729
|
untrustedContentHint: true,
|
|
730
|
+
...(typeof customAnnotations?.debugging === "boolean"
|
|
731
|
+
? { debugging: customAnnotations.debugging }
|
|
732
|
+
: {}),
|
|
671
733
|
...(customAnnotations || {}),
|
|
672
734
|
};
|
|
673
735
|
}
|
|
@@ -680,7 +742,7 @@ export function resolveWebMcpToolAnnotations(
|
|
|
680
742
|
* that unregistration works identically regardless of which implementation is
|
|
681
743
|
* actually active.
|
|
682
744
|
*
|
|
683
|
-
* Key correctness detail: `
|
|
745
|
+
* Key correctness detail: `ensurePolyfill()` is a no-op when
|
|
684
746
|
* Chrome's native `document.modelContext` already exists — it does NOT
|
|
685
747
|
* override the native API. In that situation we are talking to the native
|
|
686
748
|
* Chrome API even in "polyfill" mode. The native Chrome API only supports
|
|
@@ -735,6 +797,7 @@ export async function registerWebMcpTools(
|
|
|
735
797
|
try {
|
|
736
798
|
const toolDef: any = {
|
|
737
799
|
name: def.name,
|
|
800
|
+
title: (def as any).title || def.name,
|
|
738
801
|
description: def.description,
|
|
739
802
|
inputSchema: parseWebMcpInputSchema(def.input_schema),
|
|
740
803
|
annotations: resolveWebMcpToolAnnotations(
|
package/src/utils/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./normalize-string-list.js";
|
|
|
18
18
|
export * from "./normalize-workspace-path.js";
|
|
19
19
|
export * from "./parse-boolean-config.js";
|
|
20
20
|
export * from "./parse-stored-string-list.js";
|
|
21
|
+
export * from "./resolve-page-script-dependencies.js";
|
|
21
22
|
export * from "./retry.js";
|
|
22
23
|
export * from "./seo.js";
|
|
23
24
|
export * from "./ulid.js";
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { describe, expect, it, jest } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
PageScriptDependencySource,
|
|
4
|
+
resolvePageScriptDependencies,
|
|
5
|
+
} from "./resolve-page-script-dependencies.js";
|
|
6
|
+
|
|
7
|
+
describe("resolvePageScriptDependencies", () => {
|
|
8
|
+
const sampleDescriptors: PageScriptDependencySource[] = [
|
|
9
|
+
{
|
|
10
|
+
id: "feed-scroller",
|
|
11
|
+
src: "/pages/main/assets/feed-scroller.js",
|
|
12
|
+
global: true,
|
|
13
|
+
hasInit: true,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "x-hook-component",
|
|
17
|
+
src: "/pages/main/assets/x-hook-component.js",
|
|
18
|
+
hasInit: true,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "x-postpress-code",
|
|
22
|
+
src: "/pages/main/assets/x-postpress-code.js",
|
|
23
|
+
hasInit: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
src: "/pages/main/assets/utility-logger.js",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
it("returns empty array when descriptors are empty and no frontmatter provided", () => {
|
|
31
|
+
expect(resolvePageScriptDependencies([], null)).toEqual([]);
|
|
32
|
+
expect(resolvePageScriptDependencies(undefined as any, undefined)).toEqual(
|
|
33
|
+
[],
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("always includes global or autoload scripts even if frontmatter is empty or null", () => {
|
|
38
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, null);
|
|
39
|
+
expect(result).toHaveLength(1);
|
|
40
|
+
expect(result[0].id).toBe("feed-scroller");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("resolves dependencies specified by id in frontmatter.dependencies", () => {
|
|
44
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
45
|
+
dependencies: ["x-hook-component"],
|
|
46
|
+
});
|
|
47
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
48
|
+
"/pages/main/assets/feed-scroller.js",
|
|
49
|
+
"/pages/main/assets/x-hook-component.js",
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("resolves dependencies case-insensitively", () => {
|
|
54
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
55
|
+
dependencies: ["X-Hook-Component"],
|
|
56
|
+
});
|
|
57
|
+
expect(result.some((r) => r.id === "x-hook-component")).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("resolves dependencies specified by name", () => {
|
|
61
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
62
|
+
dependencies: ["x-postpress-code"],
|
|
63
|
+
});
|
|
64
|
+
expect(result.some((r) => r.name === "x-postpress-code")).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("resolves dependencies specified by src path", () => {
|
|
68
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
69
|
+
dependencies: ["/pages/main/assets/utility-logger.js"],
|
|
70
|
+
});
|
|
71
|
+
expect(
|
|
72
|
+
result.some((r) => r.src === "/pages/main/assets/utility-logger.js"),
|
|
73
|
+
).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("resolves dependencies with normalized relative paths", () => {
|
|
77
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
78
|
+
dependencies: ["pages/main/assets/utility-logger.js"],
|
|
79
|
+
});
|
|
80
|
+
expect(
|
|
81
|
+
result.some((r) => r.src === "/pages/main/assets/utility-logger.js"),
|
|
82
|
+
).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("resolves dependencies from frontmatter.scripts array", () => {
|
|
86
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
87
|
+
scripts: ["x-hook-component", "x-postpress-code"],
|
|
88
|
+
});
|
|
89
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
90
|
+
"/pages/main/assets/feed-scroller.js",
|
|
91
|
+
"/pages/main/assets/x-hook-component.js",
|
|
92
|
+
"/pages/main/assets/x-postpress-code.js",
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("resolves dependencies when provided as a comma-separated string", () => {
|
|
97
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
98
|
+
dependencies: "x-hook-component, x-postpress-code",
|
|
99
|
+
});
|
|
100
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
101
|
+
"/pages/main/assets/feed-scroller.js",
|
|
102
|
+
"/pages/main/assets/x-hook-component.js",
|
|
103
|
+
"/pages/main/assets/x-postpress-code.js",
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("handles string descriptors seamlessly", () => {
|
|
108
|
+
const stringDescriptors = [
|
|
109
|
+
"/pages/main/assets/global-one.js",
|
|
110
|
+
{ id: "widget", src: "/pages/main/assets/widget.js" },
|
|
111
|
+
];
|
|
112
|
+
const result = resolvePageScriptDependencies(stringDescriptors as any, {
|
|
113
|
+
dependencies: ["widget"],
|
|
114
|
+
});
|
|
115
|
+
expect(result).toHaveLength(1);
|
|
116
|
+
expect(result[0].src).toBe("/pages/main/assets/widget.js");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("deduplicates results if a global script is also explicitly requested", () => {
|
|
120
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
121
|
+
dependencies: ["feed-scroller", "x-hook-component"],
|
|
122
|
+
});
|
|
123
|
+
expect(result).toHaveLength(2);
|
|
124
|
+
expect(
|
|
125
|
+
result.filter((r) => r.src === "/pages/main/assets/feed-scroller.js"),
|
|
126
|
+
).toHaveLength(1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("gracefully ignores missing or unknown ids without throwing an error", () => {
|
|
130
|
+
const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
131
|
+
const result = resolvePageScriptDependencies(sampleDescriptors, {
|
|
132
|
+
dependencies: ["non-existent-script-id", "x-hook-component"],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
136
|
+
"/pages/main/assets/feed-scroller.js",
|
|
137
|
+
"/pages/main/assets/x-hook-component.js",
|
|
138
|
+
]);
|
|
139
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
140
|
+
warnSpy.mockRestore();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("gracefully handles completely invalid or unresolvable dependencies", () => {
|
|
144
|
+
const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
145
|
+
const result = resolvePageScriptDependencies(
|
|
146
|
+
[{ id: "known", src: "/known.js" }],
|
|
147
|
+
{ dependencies: ["unknown-a", "unknown-b"] },
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(result).toEqual([]);
|
|
151
|
+
warnSpy.mockRestore();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("graceful fallback for existing configs without id", () => {
|
|
155
|
+
const legacyDescriptors = [
|
|
156
|
+
{ src: "/pages/main/assets/feed-scroller.js", global: true },
|
|
157
|
+
{ src: "/pages/main/assets/x-hook-component.js" },
|
|
158
|
+
{ src: "/pages/main/assets/x-postpress-code.js" },
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
it("matches dependency by stem when existing config has no id", () => {
|
|
162
|
+
const result = resolvePageScriptDependencies(legacyDescriptors, {
|
|
163
|
+
dependencies: ["x-hook-component"],
|
|
164
|
+
});
|
|
165
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
166
|
+
"/pages/main/assets/feed-scroller.js",
|
|
167
|
+
"/pages/main/assets/x-hook-component.js",
|
|
168
|
+
]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("matches dependency by filename with extension when existing config has no id", () => {
|
|
172
|
+
const result = resolvePageScriptDependencies(legacyDescriptors, {
|
|
173
|
+
dependencies: ["x-postpress-code.js"],
|
|
174
|
+
});
|
|
175
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
176
|
+
"/pages/main/assets/feed-scroller.js",
|
|
177
|
+
"/pages/main/assets/x-postpress-code.js",
|
|
178
|
+
]);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("fails gracefully and renders best-effort if an id is missing from existing config", () => {
|
|
182
|
+
const warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
183
|
+
// Frontmatter requests an unknown id AND a valid legacy script
|
|
184
|
+
const result = resolvePageScriptDependencies(legacyDescriptors, {
|
|
185
|
+
dependencies: ["non-existent-widget", "x-hook-component"],
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Best effort: successfully resolves the known script and global script, ignores missing
|
|
189
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
190
|
+
"/pages/main/assets/feed-scroller.js",
|
|
191
|
+
"/pages/main/assets/x-hook-component.js",
|
|
192
|
+
]);
|
|
193
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
194
|
+
expect.stringContaining('Script dependency "non-existent-widget"'),
|
|
195
|
+
);
|
|
196
|
+
warnSpy.mockRestore();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("supports string array config without ids matching by stem", () => {
|
|
200
|
+
const stringArrayConfig = [
|
|
201
|
+
"/pages/main/assets/feed-scroller.js",
|
|
202
|
+
"/pages/main/assets/x-hook-component.js",
|
|
203
|
+
];
|
|
204
|
+
const result = resolvePageScriptDependencies(stringArrayConfig, {
|
|
205
|
+
dependencies: ["x-hook-component"],
|
|
206
|
+
});
|
|
207
|
+
expect(result.map((r) => r.src)).toEqual([
|
|
208
|
+
"/pages/main/assets/x-hook-component.js",
|
|
209
|
+
]);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("content-based auto-detection", () => {
|
|
214
|
+
it("automatically detects inline script tags in markdown content", () => {
|
|
215
|
+
const rawContent = `
|
|
216
|
+
# Title
|
|
217
|
+
Some text
|
|
218
|
+
<script type="module" src="/pages/main/assets/x-postpress-code.js"></script>
|
|
219
|
+
`;
|
|
220
|
+
const result = resolvePageScriptDependencies(
|
|
221
|
+
sampleDescriptors,
|
|
222
|
+
null,
|
|
223
|
+
rawContent,
|
|
224
|
+
);
|
|
225
|
+
expect(result.map((r) => r.src)).toContain(
|
|
226
|
+
"/pages/main/assets/x-postpress-code.js",
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("automatically detects custom element tags matching descriptors in markdown content", () => {
|
|
231
|
+
const rawContent = `
|
|
232
|
+
# Post
|
|
233
|
+
<x-postpress-code type="html"><pre>code</pre></x-postpress-code>
|
|
234
|
+
`;
|
|
235
|
+
const result = resolvePageScriptDependencies(
|
|
236
|
+
sampleDescriptors,
|
|
237
|
+
null,
|
|
238
|
+
rawContent,
|
|
239
|
+
);
|
|
240
|
+
expect(result.map((r) => r.src)).toContain(
|
|
241
|
+
"/pages/main/assets/x-postpress-code.js",
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|