Skip to content

Server Context Refactor#1696

Draft
robmoffat wants to merge 25 commits intomainfrom
server-context-refactor
Draft

Server Context Refactor#1696
robmoffat wants to merge 25 commits intomainfrom
server-context-refactor

Conversation

@robmoffat
Copy link
Member

@robmoffat robmoffat commented Nov 5, 2025

Describe your change

  • Simplified the relationships between the MessageHandlers, ServerContext and FDC3Server by combining ServerContext and FDC3Server into FDC3ServerInstance, with an abstract implementation re-used in fdc3-get-agent and fdc3-web-impl tests and the demo desktop agent itself.
  • This removes a cyclic relationship between these three classes which existed before.
  • Moved all server state into the FDC3ServerInstance class, giving a better separation of concerns with MessageHandlers: these only manage conversion to and from the DACP / WCP format.
  • Moved heartbeat state logic out of HeartbeatHandler and into FDC3ServerInstance via the heartbeatActivity method. This means that desktop agent implementations extending FDC3ServerInstance are able to customise the connection / disconnection process themselves if they think they have a better idea of the state of applications.
  • Conformance tests all passing.

Related PR

This is based on #1586 which should be merged and released first as it contains fixes for fdc3-agent-proxy.

Contributor License Agreement

  • I acknowledge that a contributor license agreement is required and that I have one in place or will seek to put one in place ASAP.

Review Checklist

  • CHANGELOG: Is a CHANGELOG.md entry included?

@robmoffat robmoffat requested a review from a team as a code owner November 5, 2025 13:13
@netlify
Copy link

netlify bot commented Nov 5, 2025

Deploy Preview for fdc3 canceled.

Name Link
🔨 Latest commit 994fdb2
🔍 Latest deploy log https://app.netlify.com/projects/fdc3/deploys/690b4db396697c0008856942

@robmoffat robmoffat changed the base branch from main to fdc3-new-conformance-2.2 November 5, 2025 13:14
@robmoffat
Copy link
Member Author

robmoffat commented Nov 5, 2025

@SeeWhatsOn. @patrocinio

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

554 passed

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

Coverage Report

Commit: 6e203cf
Base: fdc3-new-conformance-2.2@ff93907

Type Base This PR
Total Statements Coverage  97.16%  96.68% (-0.48%)
Total Branches Coverage  86%  86.17% (+0.17%)
Total Functions Coverage  96.13%  95.82% (-0.31%)
Total Lines Coverage  97.32%  96.74% (-0.58%)
Details (changed files)
FileStatementsBranchesFunctionsLines
toolbox/fdc3-for-web/fdc3-web-impl/src/AbstractFDC3ServerInstance.ts 96.63% 100% 96.36% 97.11%
toolbox/fdc3-for-web/fdc3-web-impl/src/AppRegistration.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/FDC3ServerFactory.ts 100% 33.33% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/FDC3ServerInstance.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/FDC3ServerInstanceEvents.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/PendingApp.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/BroadcastHandler.ts 96.05% 85.55% 100% 95.97%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/HeartbeatHandler.ts 92.45% 79.41% 100% 92.3%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/IntentHandler.ts 97.24% 90.62% 100% 96.85%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/OpenHandler.ts 96.34% 84.84% 100% 96.34%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/support.ts 100% 100% 100% 100%
Details (all files)
FileStatementsBranchesFunctionsLines
packages/fdc3-agent-proxy/src/DesktopAgentProxy.ts 100% 100% 100% 100%
packages/fdc3-agent-proxy/src/index.ts 100% 100% 62.5% 100%
packages/fdc3-agent-proxy/src/apps/DefaultAppSupport.ts 88% 50% 100% 88%
packages/fdc3-agent-proxy/src/channels/DefaultChannel.ts 78.94% 77.77% 71.42% 78.94%
packages/fdc3-agent-proxy/src/channels/DefaultChannelSupport.ts 98.94% 93.1% 100% 98.9%
packages/fdc3-agent-proxy/src/channels/DefaultPrivateChannel.ts 97.5% 66.66% 100% 97.5%
packages/fdc3-agent-proxy/src/heartbeat/DefaultHeartbeatSupport.ts 100% 100% 100% 100%
packages/fdc3-agent-proxy/src/intents/DefaultIntentResolution.ts 100% 100% 100% 100%
packages/fdc3-agent-proxy/src/intents/DefaultIntentSupport.ts 100% 100% 100% 100%
packages/fdc3-agent-proxy/src/listeners/AbstractListener.ts 100% 60% 100% 100%
packages/fdc3-agent-proxy/src/listeners/DefaultContextListener.ts 100% 100% 100% 100%
packages/fdc3-agent-proxy/src/listeners/DefaultIntentListener.ts 100% 77.77% 100% 100%
packages/fdc3-agent-proxy/src/listeners/DesktopAgentEventListener.ts 90.9% 83.33% 100% 90.9%
packages/fdc3-agent-proxy/src/listeners/EventListener.ts 10% 0% 0% 10%
packages/fdc3-agent-proxy/src/listeners/HeartbeatListener.ts 100% 100% 100% 100%
packages/fdc3-agent-proxy/src/listeners/PrivateChannelEventListener.ts 93.33% 72.72% 100% 93.33%
packages/fdc3-agent-proxy/src/messaging/AbstractMessaging.ts 94.59% 100% 80% 94.59%
packages/fdc3-agent-proxy/src/util/AbstractFDC3Logger.ts 100% 94.11% 100% 100%
packages/fdc3-agent-proxy/src/util/Logger.ts 100% 100% 100% 100%
packages/fdc3-agent-proxy/src/util/throwIfUndefined.ts 100% 100% 100% 100%
packages/fdc3-get-agent/src/index.ts 100% 100% 28.57% 100%
packages/fdc3-get-agent/src/messaging/MessagePortMessaging.ts 100% 100% 100% 100%
packages/fdc3-get-agent/src/messaging/message-port.ts 97.43% 86.66% 100% 97.43%
packages/fdc3-get-agent/src/sessionStorage/DesktopAgentDetails.ts 97.36% 89.47% 100% 97.36%
packages/fdc3-get-agent/src/strategies/DesktopAgentPreloadLoader.ts 100% 77.77% 100% 100%
packages/fdc3-get-agent/src/strategies/FailoverHandler.ts 100% 76.47% 100% 100%
packages/fdc3-get-agent/src/strategies/HelloHandler.ts 94% 81.25% 100% 94%
packages/fdc3-get-agent/src/strategies/IdentityValidationHandler.ts 95.65% 73.33% 100% 95.65%
packages/fdc3-get-agent/src/strategies/PostMessageLoader.ts 98.48% 86.95% 100% 98.46%
packages/fdc3-get-agent/src/strategies/Timeouts.ts 100% 100% 100% 100%
packages/fdc3-get-agent/src/strategies/getAgent.ts 100% 100% 100% 100%
packages/fdc3-get-agent/src/ui/AbstractUIComponent.ts 97.14% 71.42% 100% 97.01%
packages/fdc3-get-agent/src/ui/DefaultDesktopAgentChannelSelector.ts 100% 75% 100% 100%
packages/fdc3-get-agent/src/ui/DefaultDesktopAgentIntentResolver.ts 100% 90% 100% 100%
packages/fdc3-get-agent/src/ui/NullChannelSelector.ts 100% 100% 100% 100%
packages/fdc3-get-agent/src/ui/NullIntentResolver.ts 100% 100% 66.66% 100%
packages/fdc3-get-agent/src/util/Logger.ts 100% 100% 100% 100%
packages/fdc3-get-agent/src/util/Uuid.ts 100% 100% 100% 100%
packages/fdc3-standard/src/index.ts 91.3% 70.83% 60% 95%
packages/fdc3-standard/src/api/AppIdentifier.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/AppIntent.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/AppMetadata.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Channel.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/ContextMetadata.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/DesktopAgent.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/DisplayMetadata.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Errors.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Events.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/GetAgent.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Icon.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Image.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/ImplementationMetadata.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/IntentMetadata.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/IntentResolution.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Listener.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Methods.ts 94.18% 84.28% 96.29% 95.23%
packages/fdc3-standard/src/api/PrivateChannel.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/RecommendedChannels.ts 100% 100% 100% 100%
packages/fdc3-standard/src/api/Types.ts 100% 100% 100% 100%
packages/fdc3-standard/src/context/ContextType.ts 100% 100% 100% 100%
packages/fdc3-standard/src/intents/Intents.ts 100% 100% 100% 100%
packages/fdc3-standard/src/internal/contextConfiguration.ts 100% 100% 100% 100%
packages/fdc3-standard/src/internal/intentConfiguration.ts 100% 100% 100% 100%
packages/fdc3-standard/src/internal/typeHelpers.ts 100% 100% 100% 100%
packages/fdc3-standard/src/ui/ChannelSelector.ts 100% 100% 100% 100%
packages/fdc3-standard/src/ui/Connectable.ts 100% 100% 100% 100%
packages/fdc3-standard/src/ui/IntentResolver.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/AbstractFDC3ServerInstance.ts 96.63% 100% 96.36% 97.11%
toolbox/fdc3-for-web/fdc3-web-impl/src/AppRegistration.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/FDC3ServerFactory.ts 100% 33.33% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/FDC3ServerInstance.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/FDC3ServerInstanceEvents.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/PendingApp.ts 100% 100% 100% 100%
toolbox/fdc3-for-web/fdc3-web-impl/src/directory/BasicDirectory.ts 96.87% 84.21% 100% 96.55%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/BroadcastHandler.ts 96.05% 85.55% 100% 95.97%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/HeartbeatHandler.ts 92.45% 79.41% 100% 92.3%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/IntentHandler.ts 97.24% 90.62% 100% 96.85%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/OpenHandler.ts 96.34% 84.84% 100% 96.34%
toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/support.ts 100% 100% 100% 100%

@kriswest kriswest self-requested a review November 5, 2025 14:16
Copy link
Contributor

@kriswest kriswest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try and take a look at this and the conformance PR. I do think this needed a refactor. However, if this implementation starts seeing use in the wild, then this is a big breaking change and needs to go into a major release of the associated library... There may already be others depending on the code... I can see an argument that it might need its own versioning and project... We'll need to decide what to do and future governance.

I don't think the changes conflict in a big way with #1695 (the reference implementation should not be using a websocket when it doesn't need to) - hopefully just small resolvable conflicts. It will conflict with a PR I haven't got around to raising yet but is WIP on context cleared events - should have got it in earlier!

It'd be good to have in issue raised for the proposed changes wherever possible.

Its good to see a bit more documentation in this version - however, these changes make the README.md in /toolbox/fdc3-for-web/fdc3-web-impl in correct and it'll need a new write-up.

@github-actions
Copy link

github-actions bot commented Nov 6, 2025

554 passed

Copy link
Contributor

@kriswest kriswest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently I abandoned a review last year - closing it to drop comments I'd left you @robmoffat

* Handles messaging to apps and opening apps for ONE FDC3 environment.
* Stores all state for MessageHandlers to use.
*/
export interface FDC3ServerInstance {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FDC3ServerInstance suggests a concrete implementation rather than an interface... Perhaps better to stick to FDC3Server

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate that the demo server can start multiple instances, which recycle the same socket.io server, with different instance IDs, but that feels like an oddity rather than a pattern; it probably doesn't make sense for end-users to have multiple different instances of a DA that are each an island of interop. I imagine most implementors will find a way to have multiple windows talk to the same Desktop Agent.

Server/Client terminology is perhaps also not helping here (too many servers... and this one is actually running in a browser window). I wonder if something like DesktopAgentCore or DesktopAgentBackend would be easier to understand/intuit?

* Subclass this and implement the createInstance method with your
* implementation of FDC3ServerInstance.
*/
export abstract class AbstractFDC3ServerFactory {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What advantage is there to having this factory pattern, separate from an Implementation of an FDC3ServerInstance? The constructor it provides is opinionated about what it sets up (such as the handlers that would be used in instances). Why don't we just have an FDC3Server interface and a default implementation of it that sets up the handlers (which again conform to an interface)?

The example in demo put both the factory and instance implementations in the same file and the factory is just passing on args to the instances' constructor. Why not just construct instances directly, or even just have a static createInstance function on an FDC3Server implementation (if you regularly need to create new instances - which I don't actually think is the case. That way the implementation is on control of what handlers etc. that it sets up - that is exactly the sort of thing that is likely to vary between implementations (e.g. a firm using this needs to provide a custom IntentHandler in order to tie it into some other subsystem).

import { PendingApp } from './PendingApp';
import { Directory } from './directory/DirectoryInterface';

export enum HeartbeatActivityEvent {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth extracting these types to a new file and consolidating with AppRegistration and any others

@@ -48,19 +51,23 @@ function isRunningAppRegistration(
return !!(details as RunningAppRegistration).window;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracting the types and typeguards above into a separate file would make this easier to read

setInstanceDetails(uuid: InstanceID, meta: RunningAppRegistration | LaunchingAppRegistration): void {
setInstanceDetails(uuid: InstanceID, meta: AppRegistration): void {
// Type assertion to handle our extended type
const demoMeta = meta as DemoAppRegistration;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the previous pattern a generic was used in the ServerContext to allow implementations to customise the registration - that felt better than having overrides.

Comment on lines +311 to +312
createInstance(): FDC3ServerInstance {
return new DemoFDC3ServerInstance(this.socket, this.directory, this.handlers, this.channels);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example of this not providing any tangible benefit - its just separating default values and behaviours over 3 files making them harder to interpret. I'm not seeing an advantage, just extra complexity.

contextType: string | null;
};

export type PrivateChannelEventListener = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with the others, this type and DesktopAgentEventListener should end in Registration

Suggested change
export type PrivateChannelEventListener = {
export type PrivateChannelEventListenerRegistration = {

/**
* Return the list of all apps that have ever been registered with the ServerContext.
*/
getAllApps(): Promise<AppRegistration[]>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid confusion with retrieving app details bring 'Instance' or 'Registration' into the name:

Suggested change
getAllApps(): Promise<AppRegistration[]>;
getAllAppRegistrations(): Promise<AppRegistration[]>;

getConnectedApps(): Promise<AppRegistration[]>;

/**
* Return the list of all apps that have ever been registered with the ServerContext.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also consider updating this comment to remove 'ever been' as a long running implementation might drop older registrations, or ones it knows are gone forever (say a window closed so there no way its coming back as the same instance ever again - the instance details in SessionStorage dies with the window). Also clarify that its instances/registrations rather than app definitions.

Suggested change
* Return the list of all apps that have ever been registered with the ServerContext.
* Return the list of all app instances that have been registered with the ServerContext.

* Note, it is the implementor's job to ensure this list is
* up-to-date in the event of app crashes or disconnections.
*/
getConnectedApps(): Promise<AppRegistration[]>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
getConnectedApps(): Promise<AppRegistration[]>;
getConnectedAppRegistrations(): Promise<AppRegistration[]>;

See later comment getAllApps(), rename will help comprehension. Mmatters less here because of the 'connected', but we should be consistent.

@kriswest
Copy link
Contributor

@robmoffat Not sure why you are removing the fdc3-web-impl... I thought we were going to keep it but mark not for production use and encourage sail to fork off and create and maintain a production ready version?

Base automatically changed from fdc3-new-conformance-2.2 to main February 4, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants