penpot/frontend/src/app/plugins.cljs
Andrey Antukh f7e1bcf87f
🐛 Handle plugin errors gracefully without crashing the UI (#8810)
* 🐛 Handle plugin errors gracefully without crashing the UI

Plugin errors (like 'Set is not a constructor') were propagating to the
global error handler and showing the exception page. This fix:

- Uses a WeakMap to track plugin errors (works in SES hardened environment)
- Wraps setTimeout/setInterval handlers to mark errors and re-throw them
- Frontend global handler checks isPluginError and logs to console

Plugin errors are now logged to console with 'Plugin Error' prefix but
don't crash the main application or show the exception page.

Signed-off-by: AI Agent <agent@penpot.app>

*  Improved handling of plugin errors on initialization

*  Fix test and linter

---------

Signed-off-by: AI Agent <agent@penpot.app>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-04-01 11:37:27 +02:00

53 lines
1.6 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.plugins
"RPC for plugins runtime."
(:require
["@penpot/plugins-runtime" :as runtime]
[app.main.errors :as errors]
[app.main.features :as features]
[app.main.store :as st]
[app.plugins.api :as api]
[app.plugins.flex :as flex]
[app.plugins.format :as format]
[app.plugins.grid :as grid]
[app.plugins.library :as library]
[app.plugins.public-utils]
[app.plugins.ruler-guides :as rg]
[app.plugins.shape :as shape]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn init-plugins-runtime!
[]
(runtime/initPluginsRuntime (fn [plugin-id] (api/create-context plugin-id))))
(defn initialize
[]
(ptk/reify ::initialize
ptk/WatchEvent
(watch [_ _ stream]
(set! errors/is-plugin-error? runtime/isPluginError)
(->> stream
(rx/filter (ptk/type? ::features/initialize))
(rx/observe-on :async)
(rx/filter #(features/active-feature? @st/state "plugins/runtime"))
(rx/take 1)
(rx/tap init-plugins-runtime!)
(rx/ignore)))))
;; Prevent circular dependency
(set! flex/shape-proxy? shape/shape-proxy?)
(set! grid/shape-proxy? shape/shape-proxy?)
(set! format/shape-proxy shape/shape-proxy)
(set! rg/shape-proxy shape/shape-proxy)
(set! rg/shape-proxy? shape/shape-proxy?)
(set! shape/lib-typography-proxy? library/lib-typography-proxy?)
(set! shape/lib-component-proxy library/lib-component-proxy)