refactor: load client scripts with @JsModule instead of executeJs - #412
totally-not-ai[bot] wants to merge 8 commits into
Conversation
The collector and the dev-mode Copilot panel scripts are currently read from the classpath and sent as executeJs text. Capture that both should reach the browser as regular runtime-loaded file dependencies. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The in-browser collector and the dev-mode Copilot panel were read from the classpath, comment-stripped and sent as executeJs text by ClientResourceLoader. Load them as regular frontend modules instead: - MetricsCollectorElement declares @jsmodule for VaadinMetricsClient.js. The module defines <vaadin-metrics-collector> and only starts collecting when the server attaches the element. - The error-message flag is the element's details attribute rather than a window global set by a script prelude. - ObservabilityDevToolsHandler declares the Copilot panel as a development-only @jsmodule, replacing the per-UI injection. - ClientResourceLoader, ObservabilityDevToolsClient and the comment-stripping integrity test are removed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * frontend bundle, like any other Flow dependency, not as script text sent | ||
| * through {@code executeJs}. | ||
| */ | ||
| class ClientScriptLoadingTest { |
There was a problem hiding this comment.
All these tests seem pretty pointless
There was a problem hiding this comment.
@Artur- Agreed, removed ClientScriptLoadingTest. It only restated the @JsModule annotations and the details attribute. The ITs (StandaloneMetricsIT, ClientProblemsIT, ClientTimingIT) run against a production bundle, so they already cover the scripts actually being bundled and collecting. The JS suite keeps the check that loading the module alone installs nothing.
It only restated the @jsmodule annotations and the details attribute; the ITs cover the scripts actually loading and collecting. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * The panel itself is the development-only module declared here, which the | ||
| * frontend build picks up because this class is a dev-tools entry point. | ||
| */ | ||
| @JsModule(value = "./VaadinObservabilityDevTools.js", developmentOnly = true) |
There was a problem hiding this comment.
Before this change the panel script only reached the browser after serviceInit had passed the license and configuration checks. Now the module is bundled and registers itself with Copilot on every dev-mode page load, and the handler is always registered through the service loader. So an unlicensed or unconfigured app still gets the Observability panel in Copilot, polling an empty registry and showing "instrumentation: inactive".
If we want to keep the old behaviour (no license, no panel), the panel could ask the handler whether the kit is active before adding itself. Or the handler could answer with nothing when there is no active registry, and the panel could stay hidden in that case.
There was a problem hiding this comment.
@heruan Good catch, this now keeps the old "no license, no panel" behaviour. I combined your two suggestions:
ObservabilityDevToolsHandlerstays silent until the kit has an active registry, i.e. it is licensed andserviceInithas run. It still claims its commands, so nothing gets queued for other handlers.- The panel script no longer calls
addPanelininit. It adds itself to Copilot, once, when the first metrics or insights answer arrives. It keeps polling, so if the app restarts in the same tab with a valid license, the panel shows up.
An unlicensed or unconfigured app now gets no Observability entry in Copilot, and never sees instrumentation: inactive from an unbound kit. Tests: kitNotActive_answersNothingSoThePanelStaysHidden in ObservabilityDevToolsHandlerTest, and a case in VaadinObservabilityDevTools.test.js checking there is no panel before an answer and exactly one after.
There was a problem hiding this comment.
Would it be better if the panel always showed up and even had the correct content but all numbers would be missing + there would be a text about needing a license for it
There was a problem hiding this comment.
@Artur- Agreed, done. The panel is back to always registering with Copilot, and the "wait for the first answer" gating is gone. What changed:
MetricsServiceInitListenerrecords a failed license check inObservabilityKit.isLicenseMissing(), a new public getter next togetActiveMeterRegistry().- The meter snapshot the handler sends now includes
licensed. - When
licensedisfalse, the panel shows a notice at the top: "Observability Kit needs a license. Without one, no metrics or insights are collected", with a link to the license terms. The Insights and Metrics sections keep their usual layout but read "Not collected without a license." That replaces the hints about the insights setting and about interacting with the app, which would be misleading here.
Tests: licenseMissing_stillAnswersAndSaysSo in ObservabilityDevToolsHandlerTest, an assertion in the license test that the flag is recorded, and a JS case checking the notice appears when unlicensed and not otherwise.
The panel module is now in every development bundle with the kit, so it no longer depends on serviceInit having passed the license check. The dev-tools handler now stays silent until a registry is bound, and the panel only adds itself to Copilot once the server has answered. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Rather than hiding the panel while the kit is not active, always add it. serviceInit records a failed license check, the meter snapshot carries it as licensed=false, and the panel shows a notice with empty sections instead of reading like an idle application. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Summary
The browser metrics collector and the dev-mode Copilot panel now load as normal frontend modules through
@JsModule. Before, they were read from the classpath, stripped of comments and sent withexecuteJs. The Copilot panel also now appears when the kit has no license, with a notice that explains why its sections are empty.What changed
Breaking:
ClientResourceLoader(a public class) is removed. Code that calledClientResourceLoader.loadOnce(...)no longer compiles.Behavior change: In development mode, the Copilot "Observability" panel is now always added, even without a valid license. Before, it only appeared when the license check passed. When unlicensed, it shows a license notice and "Not collected without a license." in place of the insights and meters. This affects all dev-mode users without a license.
Behavior change:
VaadinMetricsClient.jsis now part of the frontend bundle, including production bundles. Loading the module only defines<vaadin-metrics-collector>. Collection starts only when the server attaches that element, which it still does only when client metrics are enabled.MetricsCollectorElementnow declares@JsModule("./VaadinMetricsClient.js")and no longer overridesonAttach.detailsattribute on the collector element. It no longer sets thewindow.__vaadinMicrometerDetailsglobal.ObservabilityDevToolsHandlerdeclares the Copilot panel as a development-only@JsModule. This replaces the per-UI injection thatMetricsServiceInitListeneradded. The service now registers one fewerUIInitListener.MetricsServiceInitListenerrecords the result of the license check. The dev-tools meter snapshot now includes alicensedfield.ObservabilityDevToolsClient(package-private) and the comment-stripping integrity test are removed. The panel script no longer has to avoid regex literals and URLs.Use case
An application has an internal admin/health view. The team wants to warn operators when Observability Kit is installed but not collecting anything because the license is missing:
API Changes
com.vaadin.observability.micrometer.ClientResourceLoader
com.vaadin.observability.micrometer.ObservabilityKit
com.vaadin.observability.micrometer.client.MetricsCollectorElement
com.vaadin.observability.micrometer.devtools.ObservabilityDevToolsHandler
Test summary
License check (service init):
Dev-tools handler:
licensed=falseand no metersCollector element:
detailsattribute set only when insights details are on and client errors are keptCollector script:
detailsattributeCopilot panel script: