Adding Widget Packages to the Console and /dash
A widget package is bundled into the application that shows it. Adding one is a build-time change: you add the dependency, include its definitions in the application's registry, and rebuild. There is no runtime installer and no upload. An exported dashboard is portable JSON and does not carry a widget's implementation.
A package registered in one application is not installed in any other. The authoring console and every application that views a dashboard using the widget must include the same package, or the widget appears there as a placeholder. See widget versions and compatibility.
In your own application
If you embed dashboards with @devicechain/dashboards and @devicechain/widgets, build one
registry from the built-in definitions plus your own, and give the same registry to the parser and
the renderer:
import { parseDashboardDefinition } from '@devicechain/dashboards';
import { createWidgetRegistry } from '@devicechain/widget-sdk';
import { builtinWidgetDefinitions, DashboardRenderer } from '@devicechain/widgets';
import { tankLevelWidgets } from '@acme/devicechain-tank-level';
const registry = createWidgetRegistry([...builtinWidgetDefinitions, ...tankLevelWidgets]);
const definition = parseDashboardDefinition(rawDefinition, { catalog: registry.catalog });
return <DashboardRenderer definition={definition} hub={hub} registry={registry} />;
Parsing a definition with a catalog that lacks your widgets, and then rendering with a registry that has them, does not work: the parser has already turned the widget into a placeholder. Pass the one registry to both.
createWidgetRegistry validates and seals the registry. It rejects duplicate ids (including an
attempt to reuse a built-in id), malformed descriptors, an unsupported sdkApiVersion and invalid
defaults or bindings, and it reports all of them in one error. There is no global registration, and
two registries on one page are independent. A registry holds one exact version of each id.
In the DeviceChain console and /dash
The console, the /dash viewer and the board-replay player build their registry from one shared
composition module in the frontend source tree, frontend/apps/host-widgets. None of them keeps a
list of its own, so a package added there reaches every one of them at once: the definition parser,
the option validators, the renderer, the editor's Add menu and settings panel, the synthetic preview
and the recording player.
To install a widget package:
-
Add the package to the dependencies in
frontend/apps/host-widgets/package.json, then runnpm installinfrontend/. -
In
frontend/apps/host-widgets/src/extensions.ts, import the package's definitions and list them inextensionWidgets:import type { ErasedWidgetDefinition } from '@devicechain/widget-sdk';import { tankLevelWidgets } from '@acme/devicechain-tank-level';export const extensionWidgets: readonly ErasedWidgetDefinition[] = [...tankLevelWidgets]; -
Rebuild.
npm run buildinfrontend/builds the console,/dashand the board-replay player. The console and/dashship together in the frontend container image built fromfrontend/Dockerfile; build that image and deploy it in place of the stock one (the Helm chart'sfrontend.imagevalues). If you serve the board-replay player, deploy its rebuilt bundle too.
Deploy every application together. A dashboard authored in the console with your widget shows a
placeholder in a /dash or replay build that does not include the package.
If the composition is invalid, for example two packages claim the same id or a descriptor is malformed, the registry throws one error listing every fault and the application fails as it loads, so a broken package shows up in the build you test rather than one widget at a time in production.
This list is trusted code compiled into the application. It is not a runtime installer, and a dashboard cannot add to it.
Publishing while a widget is unresolved
A widget whose package is missing, or whose stored settings version the installed package cannot read, parses to a placeholder. In the console you can still open the dashboard, move and resize the placeholder and save the draft; its stored JSON is kept exactly. Publishing a new version is blocked while any widget is unresolved, and the version history panel names each one and the reason. Install the package, or remove the widget, and then publish.
The block is in the console. The server stores dashboard definitions as opaque JSON and does not make this check.
Check a package before you ship it
Build it against packed artifacts, the way a consumer receives the SDK, not against the monorepo. The
repository's hack/widget-package-proof.sh all does this for the example package: it packs the SDK,
installs the tarballs into a copy outside the repository, builds it, and checks that the package has
no private or monorepo imports, that it resolves exactly one copy of the SDK, and that its DOM-only
entry bundles no ECharts, MapLibre or built-in widget code. It then plants a chart import in the
DOM-only entry and requires the check to fail, so a check that cannot see is caught. CI runs it on
every change; it needs Node, npm, an installed frontend (npm ci in frontend/) and access to the
npm registry. On Windows, run it with Git Bash. Use the same checks on your own package.