Skip to content
morganstanleyPublic

About

NPM Library providing FDC3 capabilities

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

6 stars

Watchers

8 watching

Forks

Latest commit

 

History

773 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

fdc3-web

Lifecycle Incubating License CI OpenSSF Scorecard

Installation

npm install @morgan-stanley/fdc3-web
npm install @morgan-stanley/fdc3-web-ui-provider

Usage

Below are common usage patterns for the @morgan-stanley/fdc3-web library, including code examples for agent access, intents, channels, and App Directory setup. These examples are based on real usage in the test-harness app.

Accessing the FDC3 Agent

In the Root Window

import { DesktopAgentFactory, getAgent } from '@morgan-stanley/fdc3-web';
import { LogLevel } from '@finos/fdc3';
import { AppResolverComponent } from '@morgan-stanley/fdc3-web-ui-provider';

const agent = await getAgent({
  failover: () =>
    new DesktopAgentFactory().createRoot({
      uiProvider: agent => Promise.resolve(new AppResolverComponent(agent, document)),
      appDirectoryEntries: ['http://localhost:4299/v2/apps'],
      applicationStrategies: [{
        canOpen: (params: OpenApplicationStrategyParams, context?: Context) => { /* define whether an app should open */ },
        open: (params: OpenApplicationStrategyParams, context?: Context) => { /* define how an app should open */ }
      }],
    }),
  // Control logging levels
  logLevels: {
    connection: LogLevel.INFO,  // Controls connection/handshake related logs
    proxy: LogLevel.WARN,       // Controls agent/proxy related logs
  }
});

In a Proxy/Child Window

import { getAgent } from '@morgan-stanley/fdc3-web';

// This will attempt to establish a connection using the FDC3 Web Connection Protocol
// given the URL of this Desktop Agent Proxy 
const agent = await getAgent();

Raising and Handling Intents

Raise an Intent

const context = { type: 'fdc3.instrument', id: { ticker: 'AAPL' } };
const resolution = await agent.raiseIntent('ViewChart', context);

Add an Intent Listener

await agent.addIntentListener('ViewChart', async context => {
  // Handle the intent
  console.log('Received context:', context);
});

Working with Channels

Join a Channel

const channel = await agent.getOrCreateChannel('myChannel');
await channel.join();

Broadcast Context on a Channel

await channel.broadcast({ type: 'fdc3.instrument', id: { ticker: 'MSFT' } });

Listen for Context on a Channel

channel.addContextListener('fdc3.instrument', context => {
  console.log('Received instrument context:', context);
});

App Directory Setup

To enable app discovery and intent resolution, provide App Directory URLs when initializing the agent in the root window:

const agent = await getAgent({
  appDirectoryEntries: ['http://localhost:4299/v2/apps'],
});

App directories can also be defined locally and pass as an array of Local App Definitions. Here we define a remote app directory that is loaded from the url and a local app directory where we pass app definitions:

const agent = await getAgent({
  appDirectoryEntries: [
    'http://localhost:4299/v2/apps',
    [{ appId: 'fdc3-workbench', url: 'https://fdc3.finos.org/toolbox/fdc3-workbench/', title: 'FDC3 Workbench' }],
    ],
});

For more advanced usage, see the test-harness example app.

Local App Directories with Live Updates

Local app directories can receive live updates via an async iterator. This is useful for dynamically adding or updating app definitions at runtime:

const updates: AsyncIterator<AppDirectoryApplication | AppDirectoryApplication[]>;

const agent = await getAgent({
  failover: () =>
    new DesktopAgentFactory().createRoot({
      appDirectoryEntries: [
        {
          host: 'my-domain.com',
          apps: [
            { appId: 'static-app', title: 'Static App', type: 'web', details: { url: 'https://example.com/static' } }
          ],
          updates,
        }
      ],
    }),
});

Singleton Apps

Apps can be configured as singletons to prevent multiple instances from being opened. When the intent resolver UI is displayed, singleton apps with an active instance will not appear in the "Open New" section. Users can only select the existing instance.

Configure singleton behavior via the hostManifests property in your app directory entry:

{
  appId: 'my-singleton-app',
  title: 'My Singleton App',
  type: 'web',
  details: { url: 'https://example.com/singleton' },
  hostManifests: {
    'MorganStanley.fdc3-web': { singleton: true }
  }
}

Custom Application Strategies

Application strategies control how apps are opened, selected, and tracked. There are three types of strategies:

Open Application Strategy

Defines how new app instances are launched. Implement IOpenApplicationStrategy:

import { subscribeToConnectionAttemptUuids } from "@morgan-stanley/fdc3-web";

const customOpenStrategy = {
  manifestKey: 'MyCustomManifest', // Optional: key to extract from hostManifests
  
  canOpen: async (params) => {
    // Return true if this strategy can open the app
    return params.appDirectoryRecord.type === 'web';
  },
  
  open: async (params) => {
    const newWindow = window.open(params.appDirectoryRecord.details.url);
    // return connectionAttemptUUID received from new window
    return new Promise(resolve => {
        const subscriber = subscribeToConnectionAttemptUuids(
            window, // the current window
            newWindow,
            connectionAttemptUuid => {
                subscriber.unsubscribe();

                resolve(connectionAttemptUuid);
            },
        );
    });
  }
};

Select Application Strategy

Defines how existing app instances are focused or brought to the foreground. Implement ISelectApplicationStrategy:

const customSelectStrategy = {
  manifestKey: 'MyCustomManifest', // Optional: key to extract from hostManifests
  
  canSelectApp: async (params) => {
    // Return true if this strategy can select/focus the app
    return true;
  },
  
  selectApp: async (params) => {
    // Focus or bring the existing app instance to the foreground
    // params.appIdentifier contains the instanceId of the target app
  }
};

New Instance Strategy

Notified whenever a new app instance completes its connection handshake and is assigned an instanceId. Unlike IOpenApplicationStrategy, this is called for every new instance regardless of how its window was created (via an IOpenApplicationStrategy, a plain window.open() call, an iframe, etc.), so it is useful for strategies that need to track or manage all app windows rather than just the ones they opened themselves. Implement INewInstanceStrategy:

const customNewInstanceStrategy = {
  onNewInstance: (params) => {
    // params.fullyQualifiedAppIdentifier contains the appId/instanceId of the new app instance
    // params.window is the window the app instance connected from, if known
    myWindowRegistry.track(params.fullyQualifiedAppIdentifier, params.window);
  }
};

Pass strategies when creating the root agent:

const agent = await getAgent({
  failover: () =>
    new DesktopAgentFactory().createRoot({
      applicationStrategies: [customOpenStrategy, customSelectStrategy, customNewInstanceStrategy],
    }),
});

Strategies are evaluated in order. The first strategy where canOpen() or canSelectApp() returns true will be used. All INewInstanceStrategy strategies are notified for every new instance.

Backoff Retry for App Directory Loading

When loading remote app directories, the agent can retry failed requests with exponential backoff:

const agent = await getAgent({
  failover: () =>
    new DesktopAgentFactory().createRoot({
      appDirectoryEntries: ['https://my-app-directory.com/v2/apps'],
      backoffRetry: {
        maxAttempts: 5,    // Maximum number of retry attempts (default: 3)
        baseDelay: 500     // Initial delay in ms, doubles with each retry (default: 250)
      }
    }),
});

With baseDelay: 500 and maxAttempts: 5, retries would occur at approximately 500ms, 1000ms, 2000ms, and 4000ms intervals.

Controlling Logging Levels

The getAgent function accepts a logLevels parameter that allows fine-grained control over logging behavior:

const agent = await getAgent({
  // other parameters...
  logLevels: {
    connection: LogLevel.INFO,  // Controls connection/handshake related logs
    proxy: LogLevel.WARN,       // Controls agent/proxy related logs
  }
});

Available log levels from @finos/fdc3 are:

  • LogLevel.DEBUG - Most verbose logging
  • LogLevel.INFO - Standard information logging
  • LogLevel.WARN - Warnings only
  • LogLevel.ERROR - Errors only
  • LogLevel.NONE - No logging

Development Notes

  • lib - The actual implementation of the fdc3 code. This library will be published for use in other applications.
  • ui-provider - A UI provider for the fdc3 library. This provides a Resolver and Channel Selector. This will be published for use in other applications.
  • test-harness - A Lit app for testing local messaging between different apps working in the same context. Will depend on lib.
  • test-harness-e2e - Playwright browser tests for the FDC3 3.0 APIs, using the test harness across application origins.

For most development running npm start will be sufficient to test implementation and cross-frame / cross origin communication. This will build and run test-harness.

Running E2E Tests

The separate test-harness-e2e project owns the Playwright configuration, tests and reports. It exercises the documented FDC3 3.0 APIs, including optional-parameter variations, channel and intent callbacks, app lifecycle operations and error cases. Shared helpers provide named steps in the tests and HTML reports, and harness controls have stable automation IDs.

Run these commands from the repository root:

# First-time setup
npm ci
npx playwright install chromium

# Run all browser automation tests
npx nx e2e test-harness-e2e

# Equivalent convenience commands
npx nx e2e test-harness
npm run test:e2e

# Open the HTML report after a run
npm run test:e2e:report

Playwright builds and starts the harness and app-directory servers automatically; you do not need to run npm start first. The suite uses Chromium and localhost ports 4200, 4299 and 4300–4305. An existing harness server can be reused outside CI; restart it after harness changes so the built apps are current.

For development and debugging:

# Run tests whose names match a pattern
npx nx e2e test-harness-e2e --grep=raiseIntent

# Show the browser while tests run
npx nx e2e test-harness-e2e --headed

# Open the interactive Playwright test runner
npx nx e2e test-harness-e2e --ui

# List tests without running them
npx nx e2e test-harness-e2e --list

The HTML report is written to playwright-report/, with JSON results in reports/playwright-results.json. Failures retain screenshots, videos and traces in test-results/. The suite includes regression tests for duplicate broadcast delivery, intent result-type filtering, rejected intent handlers, result metadata and connection options. All E2E tests run as ordinary assertions; there are no expected-failure annotations.

See the E2E suite README for the API coverage matrix, regression coverage and API explorer instructions. Browser tests run through the e2e target; npm test runs the unit tests.

npm run build:release also requires the full E2E suite to pass. Nx runs the shared Playwright target once per release build, after building the harness and type-checking the browser tests. Install Chromium before your first local release build (npx playwright install chromium; use --with-deps on Linux if system dependencies are missing). Both CI workflows install Chromium and its system dependencies before running the release build.

Running Conformance Tests

Purpose: Run the FDC3 website conformance test suite using the test harness UI.

Prerequisites

  • Ensure any popup blockers or strict cross-origin blocking are disabled for the test hosts

Start the test harness

git clone https://github.com/morganstanley/fdc3-web.git
cd fdc3-web
npm ci
npm run start

The dev server serves the test UI (commonly at http://localhost:4200).

Open the Conformance test app

  • The harness accepts query parameters to override the app directory and to force apps to open in new windows.
  • Use this example URL to run the website conformance directory and open apps in new windows:
http://localhost:4200/root-app.html?openInWindow=true&appDirectoryUrl=https://fdc3.finos.org/toolbox/fdc3-conformance/directories/website-conformance.v2.json
  • appDirectoryUrl: JSON directory used by the conformance suite (when provided, it replaces the default directories).
  • openInWindow=true: defaults the harness to open apps in new browser windows (recommended for conformance runs).

Run tests in the UI

  1. In the harness UI select the "Conformance" test app from the app dropdown.
  2. Click Add App to launch the conformance app instance.
  3. Use the conformance app UI to start individual tests or run the full suite.

Manual test: fdc3.channelChangedEvent

This test requires selecting a user channel. The conformance test setup has no UI control for changing channels, so when the test prompts you to select a channel, open the browser developer console in the conformance app window and run:

await fdc3.joinUserChannel("fdc3.channel.1")

This joins the user channel and triggers the channel-change event needed by the test.

Commands

# Clean install all package dependencies
npm ci

# build all projects
npm run build

# Run unit tests across projects
npm run test

# Run browser automation tests
npx nx e2e test-harness-e2e

# Checks the code for lint errors
npm run lint 

# Run a full build (Compile, Unit Tests, E2E Tests, Lint)
npm run build:release

# test a single project
npx nx test fdc3-web 

# test a single project in watch mode
npx nx test fdc3-web --watch

# watch tests across all projects
npm run test:watch

Development setup

We recommend using VSCode for the best development experience. We also recommend installing the following extensions

  • ESLint
  • Code Spell Checker

If you wish to use another editors there are no known restrictions.

About

NPM Library providing FDC3 capabilities

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

6 stars

Watchers

8 watching

Forks

Releases

Packages

Used by

Contributors

Languages