Skip to content

feat(lightspeed): restructure plugin to make NFS the default entry point with OFS at legacy subpath#3513

Open
its-mitesh-kumar wants to merge 5 commits into
redhat-developer:mainfrom
its-mitesh-kumar:feat/lightspeed-nfs-ga
Open

feat(lightspeed): restructure plugin to make NFS the default entry point with OFS at legacy subpath#3513
its-mitesh-kumar wants to merge 5 commits into
redhat-developer:mainfrom
its-mitesh-kumar:feat/lightspeed-nfs-ga

Conversation

@its-mitesh-kumar

@its-mitesh-kumar its-mitesh-kumar commented Jun 20, 2026

Copy link
Copy Markdown
Member

Description

Restructures the Lightspeed plugin source layout to make the New Frontend System (NFS) the primary/default entry point, while preserving full backward compatibility for the old frontend system via a dedicated ./legacy subpath export.
Previously, the NFS code lived under src/alpha/ and was accessed via @red-hat-developer-hub/backstage-plugin-lightspeed/alpha, while the legacy code was the default export at the package root. This PR inverts that relationship — NFS is now the default (import from '@red-hat-developer-hub/backstage-plugin-lightspeed'), and legacy components are accessed via @red-hat-developer-hub/backstage-plugin-lightspeed/legacy.
All public API symbols have been promoted from @alpha to @public, signaling production readiness for the NFS mode.

Also

This PR adds dedicated Module Federation entry points for the Lightspeed FAB (Floating Action Button) module and the Translations module, and fixes a React Context duplication bug that occurs across MF chunk boundaries in the New Frontend System (NFS).
These changes are required because dynamicFrontendFeaturesLoader() in Backstage NFS only loads default exports from each exposed module. The existing lightspeedFABModule and lightspeedTranslationsModule are named exports from ./src/alpha/index.tsx, which means they cannot be discovered and loaded by the NFS dynamic plugin loader without a dedicated entry point that re-exports them as default.

Key Changes

Change Reason
src/alpha/index.tsxsrc/index.tsx NFS plugin becomes the default package entry point, aligning with Backstage's direction of NFS as the standard frontend architecture
src/index.tssrc/legacyExports.ts + new ./legacy subpath Old frontend system components are preserved but moved out of the default path to signal deprecation trajectory
New Legacy scalprum exposed module Required for the dynamic plugin installer to resolve legacy mount point imports (module: Legacy) in the old frontend system
app-config.dynamic.yaml adds module: Legacy Without explicit module targeting, the dynamic plugin loader cannot distinguish which exposed module contains legacy components vs. NFS extensions
@alpha@public on all exports NFS mode is production-ready; alpha visibility blocked adoption
src/lightspeedFABModuleExport.ts + src/lightspeedTranslationsModuleExport.ts (new) NFS loader only loads default exports. lightspeedFABModule and lightspeedTranslationsModule are named exports, invisible to dynamicFrontendFeaturesLoader(). These files re-export them as default.
src/components/LightspeedDrawerContext.tsx — globalThis singleton React Context duplication across MF boundaries. Each remote chunk gets its own module scope, causing createContext() to produce duplicates. "Dock to Window" mode failed because provider and consumer held different Context objects. The globalThis singleton ensures one shared instance.
Workspace resolutions for react-router, @react-stately Resolves peer dependency conflicts introduced by updated transitive dependencies

Migration Guide

For NFS consumers (new):

// Before
import { lightspeedFABModule } from '@red-hat-developer-hub/backstage-plugin-lightspeed/alpha';
// After
import { lightspeedFABModule } from '@red-hat-developer-hub/backstage-plugin-lightspeed';

For legacy consumers:

// Before
import { LightspeedPage, LightspeedDrawerProvider } from '@red-hat-developer-hub/backstage-plugin-lightspeed';
// After
import { LightspeedPage, LightspeedDrawerProvider } from '@red-hat-developer-hub/backstage-plugin-lightspeed/legacy';

Test it on RHDH-Local Steps

Prerequisites


Build the Lightspeed dynamic plugin

    cd workspaces/lightspeed/plugins/lightspeed
    yarn install
   sudo npx @red-hat-developer-hub/cli plugin export --dev --dynamic-plugins-root /Users/mitkumar/Desktop/rhdh-local/local-plugins

NFS mode

1. Enable NFS mode in RHDH-local

Ensure the following environment variables are set in /path/to/rhdh-local/.env:

APP_CONFIG_app_packageName=app-next
ENABLE_STANDARD_MODULE_FEDERATION=true

2. Configure dynamic plugins

In your dynamic-plugins.override.yaml (or equivalent), ensure the local lightspeed frontend is enabled:

plugins:
  # Disable OCI lightspeed frontend (if present in defaults)
  - package: 'oci://registry.access.redhat.com/rhdh/red-hat-developer-hub-backstage-plugin-lightspeed@sha256:...'
    disabled: true
  # Enable local build
  - package: './local-plugins/red-hat-developer-hub-backstage-plugin-lightspeed'
    disabled: false

3. Configure NFS extensions

Create or update configs/app-config/app-config.local.yaml:

app:
  extensions:
    - app-root-wrapper:app/drawer
    - app-root-wrapper:app/lightspeed-fab
    - translation:app/lightspeed-translations
    - api:app/app-language:
        config:
          availableLanguages: ['en', 'de', 'es', 'fr', 'it', 'ja']
          defaultLanguage: 'en'

Important: The order of app-root-wrapper extensions matters.
app/drawer must come before app/lightspeed-fab to ensure correct React Context nesting.


4. Volume Mount for Lightspeed Configs

The developer-lightspeed/compose.yaml file must exist with the following content to mount Lightspeed configuration files into the containers:

services:
  install-dynamic-plugins:
    volumes:
      - ./developer-lightspeed/configs:/opt/app-root/src/developer-lightspeed/configs:Z

  rhdh:
    volumes:
      - ./developer-lightspeed/configs:/opt/app-root/src/developer-lightspeed/configs:Z

This is required for both NFS and OFS (Old Frontend System) because the dynamic plugin installer needs access to developer-lightspeed/configs/dynamic-plugins/dynamic-plugins.lightspeed.yaml — which is referenced as an includes path in dynamic-plugins.override.yaml.

Without this volume mount, the installer either fails to resolve the include path or silently skips the Lightspeed plugin configuration entirely.

When starting RHDH-local, always include this compose file:

podman compose -f compose.yaml -f developer-lightspeed/compose.yaml -f compose-dynamic-plugins-root.yaml up -d

Note (rhdh-local gap): This file was removed from the upstream rhdh-local repository but is still required for Lightspeed to function. It should be restored in the repo so that podman compose up works out of the box for Lightspeed users without manual file creation.

4. Start RHDH-local

cd /path/to/rhdh-local
rm -f dynamic-plugins-root/install-dynamic-plugins.lock # if already exist
podman compose -f compose.yaml -f developer-lightspeed/compose.yaml -f compose-dynamic-plugins-root.yaml up -d

5. Verify module federation endpoints

Once RHDH is running, confirm the new modules are exposed:

http://localhost:7007/.backstage/dynamic-features/remotes d

Expected: entries for LightspeedFABModule and LightspeedTranslationsModule


6. Cleanup

podman compose -f compose.yaml -f developer-lightspeed/compose.yaml -f compose-dynamic-plugins-root.yaml down

Steps to run in OFS mode

1. Configure dynamic plugins for OFS

In developer-lightspeed/configs/dynamic-plugins/dynamic-plugins.lightspeed.yaml, the Lightspeed plugin entry must include pluginConfig.dynamicPlugins.frontend with explicit module: Legacy on all mount points and routes:

    plugins:
      - package: './local-plugins/red-hat-developer-hub-backstage-plugin-lightspeed'
        disabled: false
        pluginConfig:
          dynamicPlugins:
            frontend:
              red-hat-developer-hub.backstage-plugin-lightspeed:
                translationResources:
                  - importName: lightspeedTranslations
                    module: Alpha
                    ref: lightspeedTranslationRef
                dynamicRoutes:
                  - path: /intelligent-assistant
                    importName: LightspeedPage
                    module: Legacy
                mountPoints:
                  - mountPoint: application/listener
                    importName: LightspeedFAB
                    module: Legacy
                  - mountPoint: application/provider
                    importName: LightspeedDrawerProvider
                    module: Legacy
                  - mountPoint: application/internal/drawer-state
                    importName: LightspeedDrawerStateExposer
                    module: Legacy
                  - mountPoint: application/internal/drawer-content
                    importName: LightspeedChatContainer
                    module: Legacy
                    config:
                      id: lightspeed
                      priority: 100
      - package: 'oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-lightspeed-backend:bs_1.49.4__2.8.5'
        disabled: false

2. Ensure volume mount exists

The developer-lightspeed/compose.yaml file must exist (see "Volume Mount for Lightspeed Configs" section above).

2. Start RHDH-local

    cd /path/to/rhdh-local
    rm -f dynamic-plugins-root/install-dynamic-plugins.lock # if already exist
    podman compose -f compose.yaml -f developer-lightspeed/compose.yaml -f compose-dynamic-plugins-root.yaml up -d

UI after changes

OFS

S_.2026-06-21.at.5.54.46.PM.mov

NFS

S_.2026-06-21.at.6.06.48.PM.mov

Fixed

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

… for FAB and translations

- Add lightspeedFABModuleExport.ts and lightspeedTranslationsModuleExport.ts
  as dedicated default-export entry points required by dynamicFrontendFeaturesLoader
- Update package.json exports, typesVersions, and scalprum.exposedModules to
  register both modules as separate Module Federation containers
- Fix React Context duplication across MF boundaries using globalThis singleton
  pattern in LightspeedDrawerContext, resolving "useLightspeedDrawerContext must
  be used within a LightspeedDrawerProvider" error in Dock-to-Window mode

Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@rhdh-gh-app

rhdh-gh-app Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
app-legacy workspaces/lightspeed/packages/app-legacy none v0.0.28
app workspaces/lightspeed/packages/app none v0.0.27
@red-hat-developer-hub/backstage-plugin-lightspeed workspaces/lightspeed/plugins/lightspeed minor v2.9.1

@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.28571% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.62%. Comparing base (c3c2966) to head (b3cbfd2).
⚠️ Report is 4 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3513   +/-   ##
=======================================
  Coverage   53.62%   53.62%           
=======================================
  Files        2256     2255    -1     
  Lines       85870    85880   +10     
  Branches    24186    24184    -2     
=======================================
+ Hits        46046    46053    +7     
- Misses      38333    38336    +3     
  Partials     1491     1491           
Flag Coverage Δ *Carryforward flag
adoption-insights 83.70% <ø> (ø) Carriedforward from 3f0fdd0
ai-integrations 67.95% <ø> (ø) Carriedforward from 3f0fdd0
app-defaults 69.79% <ø> (ø) Carriedforward from 3f0fdd0
augment 46.39% <ø> (ø) Carriedforward from 3f0fdd0
boost 100.00% <ø> (ø) Carriedforward from 3f0fdd0
bulk-import 72.46% <ø> (ø) Carriedforward from 3f0fdd0
cost-management 14.10% <ø> (ø) Carriedforward from 3f0fdd0
dcm 61.79% <ø> (ø) Carriedforward from 3f0fdd0
extensions 61.53% <ø> (ø) Carriedforward from 3f0fdd0
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 3f0fdd0
global-header 59.71% <ø> (ø) Carriedforward from 3f0fdd0
homepage 49.84% <ø> (ø) Carriedforward from 3f0fdd0
install-dynamic-plugins 56.23% <ø> (ø) Carriedforward from 3f0fdd0
konflux 91.49% <ø> (ø) Carriedforward from 3f0fdd0
lightspeed 68.58% <64.28%> (+<0.01%) ⬆️
mcp-integrations 85.46% <ø> (ø) Carriedforward from 3f0fdd0
orchestrator 37.75% <ø> (ø) Carriedforward from 3f0fdd0
quickstart 63.76% <ø> (ø) Carriedforward from 3f0fdd0
sandbox 79.56% <ø> (ø) Carriedforward from 3f0fdd0
scorecard 83.96% <ø> (ø) Carriedforward from 3f0fdd0
theme 61.26% <ø> (ø) Carriedforward from 3f0fdd0
translations 7.25% <ø> (ø) Carriedforward from 3f0fdd0
x2a 78.68% <ø> (ø) Carriedforward from 3f0fdd0

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c3c2966...b3cbfd2. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…path export

Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
@its-mitesh-kumar its-mitesh-kumar changed the title feat(lightspeed): expose dedicated NFS module federation entry points… feat(lightspeed): restructure plugin to make NFS the default entry point with OFS at legacy subpath Jun 21, 2026
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

@its-mitesh-kumar

Copy link
Copy Markdown
Member Author

<img width="1512" height="982" alt="S_ 2026-06-22 at 10 36 35 AM" src="https://github.com/user-attachments/assets/3014e58e-1f4d-41a2-9799-a35681d0d209" /

Some styles issue is there, need some cleaning. Fixing that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant