Skip to content
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions src/api/DesktopAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { ImplementationMetadata } from './ImplementationMetadata';
import { PrivateChannel } from './PrivateChannel';
import { AppIdentifier } from './AppIdentifier';
import { AppMetadata } from './AppMetadata';
import { Intent } from '../intents/Intents';
import { ContextType } from '../context/ContextType';

/**
* A Desktop Agent is a desktop component (or aggregate of components) that serves as a
Expand Down Expand Up @@ -125,7 +127,7 @@ export interface DesktopAgent {
* // }
* ```
*/
findIntent(intent: string, context?: Context, resultType?: string): Promise<AppIntent>;
findIntent(intent: Intent, context?: Context, resultType?: string): Promise<AppIntent>;

/**
* Find all the available intents for a particular context, and optionally a desired result context type.
Expand Down Expand Up @@ -234,7 +236,7 @@ export interface DesktopAgent {
*
* If you wish to raise an Intent without a context, use the `fdc3.nothing` context type. This type exists so that apps can explicitly declare support for raising an intent without context.
*
* Returns an `IntentResolution` object with details of the app instance that was selected (or started) to respond to the intent.
* Returns an `IntentResolution` object with details of the app instance that was selected (or started) to respond to the intent.
*
* Issuing apps may optionally wait on the promise that is returned by the `getResult()` member of the `IntentResolution`. This promise will resolve when the _receiving app's_ intent handler function returns and resolves a promise. The Desktop Agent resolves the issuing app's promise with the Context object, Channel object or void that is provided as resolution within the receiving app. The Desktop Agent MUST reject the issuing app's promise, with a string from the `ResultError` enumeration, if: (1) the intent handling function's returned promise rejects, (2) the intent handling function doesn't return a valid response (a promise or void), or (3) the returned promise resolves to an invalid type.
*
Expand Down Expand Up @@ -268,7 +270,7 @@ export interface DesktopAgent {
* }
* ```
*/
raiseIntent(intent: string, context: Context, app?: AppIdentifier): Promise<IntentResolution>;
raiseIntent(intent: Intent, context: Context, app?: AppIdentifier): Promise<IntentResolution>;

/**
* Finds and raises an intent against apps registered with the desktop agent based on the type of the specified context data example.
Expand Down Expand Up @@ -346,7 +348,7 @@ export interface DesktopAgent {
* });
* ```
*/
addIntentListener(intent: string, handler: IntentHandler): Promise<Listener>;
addIntentListener(intent: Intent, handler: IntentHandler): Promise<Listener>;

/**
* Adds a listener for incoming context broadcasts from the Desktop Agent (via a User channel or `fdc3.open`API call. If the consumer is only interested in a context of a particular type, they can they can specify that type. If the consumer is able to receive context of any type or will inspect types received, then they can pass `null` as the `contextType` parameter to receive all context types.
Expand All @@ -371,7 +373,7 @@ export interface DesktopAgent {
* });
* ```
*/
addContextListener(contextType: string | null, handler: ContextHandler): Promise<Listener>;
addContextListener(contextType: ContextType | null, handler: ContextHandler): Promise<Listener>;

/**
* Retrieves a list of the User channels available for the app to join.
Expand Down Expand Up @@ -561,7 +563,7 @@ export interface DesktopAgent {
* await fdc3.raiseIntent("StartChat", context, appIntent.apps[0].name);
* ```
*/
raiseIntent(intent: string, context: Context, name: string): Promise<IntentResolution>;
raiseIntent(intent: Intent, context: Context, name: string): Promise<IntentResolution>;

/**
* @deprecated version of `raiseIntentForContext` that targets an app by by name rather than `AppIdentifier`. Provided for backwards compatibility with versions FDC3 standard <2.0.
Expand Down
4 changes: 3 additions & 1 deletion src/api/IntentMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
* Copyright FINOS FDC3 contributors - see NOTICE file
*/

import { Intent } from '../intents/Intents';

/**
* Intent descriptor
*/
export interface IntentMetadata {
/** The unique name of the intent that can be invoked by the raiseIntent call */
readonly name: string;
readonly name: Intent;

/** Display name for the intent.
* @deprecated Use the intent name for display as display name may vary for
Expand Down
3 changes: 2 additions & 1 deletion src/api/IntentResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { IntentResult } from './Types';
import { AppIdentifier } from './AppIdentifier';
import { Intent } from '../intents/Intents';

/**
* IntentResolution provides a standard format for data returned upon resolving an intent.
Expand Down Expand Up @@ -43,7 +44,7 @@ export interface IntentResolution {
* The intent that was raised. May be used to determine which intent the user
* chose in response to `fdc3.raiseIntentForContext()`.
*/
readonly intent: string;
readonly intent: Intent;
/**
* The version number of the Intents schema being used.
*/
Expand Down
38 changes: 34 additions & 4 deletions src/api/Methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import {
ImplementationMetadata,
AppMetadata,
PrivateChannel,
Intent,
StandardContextType,
StandardIntent,
ContextTypeFor,
ContextType,
} from '..';
import { IntentsConfiguration, StandardContextsSet, StandardIntentsSet } from '../intents/IntentsConfiguration';

const DEFAULT_TIMEOUT = 5000;

Expand Down Expand Up @@ -74,7 +80,7 @@ export function open(app: AppIdentifier | string, context?: Context): Promise<Ap
}
}

export function findIntent(intent: string, context?: Context, resultType?: string): Promise<AppIntent> {
export function findIntent(intent: Intent, context?: Context, resultType?: string): Promise<AppIntent> {
return rejectIfNoGlobal(() => window.fdc3.findIntent(intent, context, resultType));
}

Expand All @@ -86,7 +92,7 @@ export function broadcast(context: Context): Promise<void> {
return rejectIfNoGlobal(() => window.fdc3.broadcast(context));
}

export function raiseIntent(intent: string, context: Context, app?: AppIdentifier | string): Promise<IntentResolution> {
export function raiseIntent(intent: Intent, context: Context, app?: AppIdentifier | string): Promise<IntentResolution> {
if (isString(app)) {
return rejectIfNoGlobal(() => window.fdc3.raiseIntent(intent, context, app));
} else {
Expand All @@ -102,12 +108,12 @@ export function raiseIntentForContext(context: Context, app?: AppIdentifier | st
}
}

export function addIntentListener(intent: string, handler: IntentHandler): Promise<Listener> {
export function addIntentListener(intent: Intent, handler: IntentHandler): Promise<Listener> {
return rejectIfNoGlobal(() => window.fdc3.addIntentListener(intent, handler));
}

export function addContextListener(
contextTypeOrHandler: string | null | ContextHandler,
contextTypeOrHandler: ContextType | null | ContextHandler,
handler?: ContextHandler
): Promise<Listener> {
//Handle (deprecated) function signature that allowed contextType argument to be omitted
Expand Down Expand Up @@ -178,6 +184,30 @@ export function findInstances(app: AppIdentifier): Promise<AppIdentifier[]> {
return rejectIfNoGlobal(() => window.fdc3.findInstances(app));
}

/**
* Check if the given context is a standard context type.
* @param contextType
*/
export function isStandardContextType(contextType: ContextType): contextType is StandardContextType {
return StandardContextsSet.has(contextType as StandardContextType);
}

/**
* Check if the given intent is a standard intent.
* @param intent
*/
export function isStandardIntent(intent: Intent): intent is StandardIntent {
return StandardIntentsSet.has(intent as StandardIntent);
}

/**
* Get the possible context types for a given intent.
* @param intent
*/
export function getPossibleContextsForIntent<I extends StandardIntent>(intent: I): ContextTypeFor<I>[] {
Comment thread
andreifloricel marked this conversation as resolved.
Outdated
return IntentsConfiguration[intent] ?? [];
}

/**
* Compare numeric semver version number strings (in the form `1.2.3`).
*
Expand Down
48 changes: 46 additions & 2 deletions src/context/ContextType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,52 @@
* SPDX-License-Identifier: Apache-2.0
* Copyright FINOS FDC3 contributors - see NOTICE file
*/

/**
* @see https://fdc3.finos.org/docs/context/spec#standard-context-types
*/
export type StandardContextType =
| 'fdc3.action'
| 'fdc3.chart'
| 'fdc3.chat.initSettings'
| 'fdc3.chat.message'
| 'fdc3.chat.room'
| 'fdc3.chat.searchCriteria'
| 'fdc3.contact'
| 'fdc3.contactList'
| 'fdc3.country'
| 'fdc3.currency'
| 'fdc3.email'
| 'fdc3.instrument'
| 'fdc3.instrumentList'
| 'fdc3.interaction'
| 'fdc3.message'
| 'fdc3.organization'
| 'fdc3.portfolio'
| 'fdc3.position'
| 'fdc3.nothing'
| 'fdc3.timerange'
| 'fdc3.transactionResult'
| 'fdc3.valuation';

/**
* @see https://fdc3.finos.org/docs/context/spec#standard-context-types
*/
export type ExperimentalContextType =
| 'fdc3.order'
| 'fdc3.orderList'
| 'fdc3.product'
| 'fdc3.trade'
| 'fdc3.tradeList';

/**
* @see https://fdc3.finos.org/docs/context/spec
*/
export type ContextType = StandardContextType | ExperimentalContextType | (string & {});

/**
* @deprecated Use {@link StandardContextType} instead
*/
export enum ContextTypes {
Chart = 'fdc3.chart',
ChatInitSettings = 'fdc3.chat.initSettings',
Expand All @@ -23,5 +69,3 @@ export enum ContextTypes {
TransactionResult = 'fdc3.transactionResult',
Valuation = 'fdc3.valuation',
}

export type ContextType = ContextTypes | string;
41 changes: 41 additions & 0 deletions src/intents/Intents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,47 @@
* SPDX-License-Identifier: Apache-2.0
* Copyright FINOS FDC3 contributors - see NOTICE file
*/

import { IntentsConfiguration } from './IntentsConfiguration';

/**
* @see https://fdc3.finos.org/docs/intents/spec#standard-intents
*/
export type StandardIntent =
| 'CreateInteraction'
| 'SendChatMessage'
| 'StartCall'
| 'StartChat'
| 'StartEmail'
| 'ViewAnalysis'
| 'ViewChat'
| 'ViewChart'
| 'ViewContact'
| 'ViewHoldings'
| 'ViewInstrument'
| 'ViewInteractions'
| 'ViewMessages'
| 'ViewNews'
| 'ViewOrders'
| 'ViewProfile'
| 'ViewQuote'
| 'ViewResearch';

/**
* @see https://fdc3.finos.org/docs/intents/spec
*/
export type Intent = StandardIntent | (string & {});

/**
* Typed possible context for a given intent
*
* @example `ContextTypeFor<'StartCall'>` is equivalent to `'fdc3.contact' | 'fdc3.contactList' | 'fdc3.nothing'`
*/
export type ContextTypeFor<I extends StandardIntent> = typeof IntentsConfiguration[I][number];

/**
* @deprecated Use {@link StandardIntent} instead
*/
export enum Intents {
CreateInteraction = 'CreateInteraction',
SendChatMessage = 'SendChatMessage',
Expand Down
69 changes: 69 additions & 0 deletions src/intents/IntentsConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { StandardIntent } from './Intents';
import { StandardContextType } from '../context/ContextType';

export const IntentsConfiguration = {
CreateInteraction: ['fdc3.contactList', 'fdc3.interaction', 'fdc3.nothing'],
StartCall: ['fdc3.contact', 'fdc3.contactList', 'fdc3.nothing'],
StartChat: ['fdc3.contact', 'fdc3.contactList', 'fdc3.chat.initSettings', 'fdc3.nothing'],
StartEmail: ['fdc3.email', 'fdc3.nothing'],
SendChatMessage: ['fdc3.chat.message', 'fdc3.nothing'],
ViewAnalysis: ['fdc3.instrument', 'fdc3.organization', 'fdc3.portfolio', 'fdc3.nothing'],
ViewChat: ['fdc3.chat.room', 'fdc3.contact', 'fdc3.contactList', 'fdc3.nothing'],
ViewChart: [
'fdc3.chart',
'fdc3.instrument',
'fdc3.instrumentList',
'fdc3.portfolio',
'fdc3.position',
'fdc3.nothing',
],
ViewContact: ['fdc3.contact', 'fdc3.nothing'],
ViewHoldings: ['fdc3.instrument', 'fdc3.instrumentList', 'fdc3.organization', 'fdc3.nothing'],
ViewInstrument: ['fdc3.instrument', 'fdc3.nothing'],
ViewInteractions: ['fdc3.contact', 'fdc3.instrument', 'fdc3.organization', 'fdc3.nothing'],
ViewMessages: ['fdc3.chat.searchCriteria', 'fdc3.nothing'],
ViewNews: [
'fdc3.country',
'fdc3.instrument',
'fdc3.instrumentList',
'fdc3.organization',
'fdc3.portfolio',
'fdc3.nothing',
],
ViewOrders: ['fdc3.contact', 'fdc3.instrument', 'fdc3.organization', 'fdc3.nothing'],
ViewProfile: ['fdc3.contact', 'fdc3.organization', 'fdc3.nothing'],
ViewQuote: ['fdc3.instrument', 'fdc3.nothing'],
ViewResearch: ['fdc3.contact', 'fdc3.instrument', 'fdc3.organization', 'fdc3.nothing'],
};
Copy link
Copy Markdown
Contributor Author

@andreifloricel andreifloricel Feb 16, 2024

Choose a reason for hiding this comment

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

I tried to "trick" TypeScript to compile this, to no avail.
And TSDX seems to be more or less dead...

// TODO - TSDX's Prettier version does not support 'satisfies' construct
// that's unfortunate because this syntax ensures that the IntentsConfiguration Map is always kep in sync with the StandardIntent type
// satisfies Record<StandardIntent, StandardContextType[]>;

const STANDARD_CONTEXT_TYPES: Readonly<StandardContextType[]> = [
'fdc3.action',
'fdc3.chart',
'fdc3.chat.initSettings',
'fdc3.chat.message',
'fdc3.chat.room',
'fdc3.chat.searchCriteria',
'fdc3.contact',
'fdc3.contactList',
'fdc3.country',
'fdc3.currency',
'fdc3.email',
'fdc3.instrument',
'fdc3.instrumentList',
'fdc3.interaction',
'fdc3.message',
'fdc3.organization',
'fdc3.portfolio',
'fdc3.position',
'fdc3.nothing',
'fdc3.timerange',
'fdc3.transactionResult',
'fdc3.valuation',
] as const;

// used internally to check if a given intent/context is a standard one
export const StandardIntentsSet = new Set(Object.keys(IntentsConfiguration) as StandardIntent[]);
export const StandardContextsSet = new Set(STANDARD_CONTEXT_TYPES);
Loading