From 2b20e62cabfd24abce32b2413af6e9272f9337e5 Mon Sep 17 00:00:00 2001
From: Giles Roadnight <10414642+Roaders@users.noreply.github.com>
Date: Fri, 19 Jul 2024 15:30:28 +0100
Subject: [PATCH 1/2] Use TS Morph to generate type predicates

---
 code-generation/generate-type-predicates.ts | 50 ++++++++++++++
 code-generation/tsconfig.json               | 37 +++++++++++
 package-lock.json                           | 73 +++++++++++++++++++++
 package.json                                |  6 +-
 4 files changed, 165 insertions(+), 1 deletion(-)
 create mode 100644 code-generation/generate-type-predicates.ts
 create mode 100644 code-generation/tsconfig.json

diff --git a/code-generation/generate-type-predicates.ts b/code-generation/generate-type-predicates.ts
new file mode 100644
index 000000000..b68424339
--- /dev/null
+++ b/code-generation/generate-type-predicates.ts
@@ -0,0 +1,50 @@
+import { InterfaceDeclaration, MethodDeclaration, Project, SyntaxKind } from "ts-morph"
+
+// open a new project with just BrowserTypes as the only source file 
+const project = new Project();
+const sourceFile = project.addSourceFileAtPath("./src/api/BrowserTypes.ts")
+
+//get a list of all interfaces in the file
+const interfaces = sourceFile.getChildrenOfKind(SyntaxKind.InterfaceDeclaration);
+
+// get a list of all conversion functions in the Convert class
+const convert = sourceFile.getClass("Convert");
+const convertFunctions = (convert?.getChildrenOfKind(SyntaxKind.MethodDeclaration) ?? []).filter(func => func.getReturnType().getText() === "string");
+
+// generate a list of Interfaces that have an associated conversion function
+const matchedInterfaces = convertFunctions.map(func => {
+    const valueParameter = func.getParameter("value");
+
+    const matchingInterface = interfaces.find(interfaceNode => {
+        return valueParameter?.getType().getText(valueParameter) === interfaceNode.getName();
+    });
+
+
+    if(matchingInterface != null) {
+        return {func, matchingInterface};
+    }
+
+    return undefined;
+}).filter(((value => value != null) as <T>(value: T | null | undefined) => value is T));
+
+// write a type predicate for each matched interface
+matchedInterfaces.forEach(matched => writePredicate(matched.matchingInterface, matched.func));
+
+
+function writePredicate(matchingInterface: InterfaceDeclaration, func: MethodDeclaration): void{
+    const predicateName = `is${matchingInterface.getName()}`;
+
+    sourceFile.addStatements(`export function ${predicateName}(value: any): value is ${matchingInterface.getName()} {
+    try{
+        Convert.${func.getName()}(value);
+        return true;
+    } catch(e: any){
+        return false; 
+    }
+}`);
+
+}
+
+sourceFile.formatText();
+
+project.saveSync();
diff --git a/code-generation/tsconfig.json b/code-generation/tsconfig.json
new file mode 100644
index 000000000..7153e3e16
--- /dev/null
+++ b/code-generation/tsconfig.json
@@ -0,0 +1,37 @@
+{
+    // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
+   "files": ["generate-type-predicates.ts"],
+    "compilerOptions": {
+    "module": "CommonJS",
+      "lib": ["dom", "esnext"],
+      "importHelpers": true,
+      // output .d.ts declaration files for consumers
+      "declaration": true,
+      "outDir": "dist",
+      // output .js.map sourcemap files for consumers
+      "sourceMap": true,
+      // match output dir to input dir. e.g. dist/index instead of dist/src/index
+      "rootDir": "./",
+      // stricter type-checking for stronger correctness. Recommended by TS
+      "strict": true,
+      // linter checks for common issues
+      "noImplicitReturns": true,
+      "noFallthroughCasesInSwitch": true,
+      // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
+      "noUnusedLocals": true,
+      "noUnusedParameters": true,
+      // use Node's module resolution algorithm, instead of the legacy TS one
+      "moduleResolution": "node",
+      // transpile JSX to React.createElement
+      "jsx": "react",
+      // interop between ESM and CJS modules. Recommended by TS
+      "esModuleInterop": true,
+      // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS
+      "skipLibCheck": true,
+      // error out if import and file system have a casing mismatch. Recommended by TS
+      "forceConsistentCasingInFileNames": true,
+      // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc`
+      "noEmit": true,
+    }
+  }
+  
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bf8bd2ebf..1fe6e927e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,6 +31,8 @@
         "rimraf": "^5.0.5",
         "rollup": "4.22.4",
         "ts-jest": "29.1.2",
+        "ts-morph": "^23.0.0",
+        "ts-node": "^10.9.2",
         "tslib": "^2.0.1",
         "typescript": "^4.0.3"
       },
@@ -1898,6 +1900,35 @@
         "node": ">= 10"
       }
     },
+    "node_modules/@ts-morph/common": {
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.24.0.tgz",
+      "integrity": "sha512-c1xMmNHWpNselmpIqursHeOHHBTIsJLbB+NuovbTTRCNiTLEr/U9dbJ8qy0jd/O2x5pc3seWuOUN5R2IoOTp8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-glob": "^3.3.2",
+        "minimatch": "^9.0.4",
+        "mkdirp": "^3.0.1",
+        "path-browserify": "^1.0.1"
+      }
+    },
+    "node_modules/@ts-morph/common/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/@tsconfig/node10": {
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@@ -3169,6 +3200,13 @@
         "node": ">= 0.12.0"
       }
     },
+    "node_modules/code-block-writer": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.1.tgz",
+      "integrity": "sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/collect-v8-coverage": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
@@ -6690,6 +6728,22 @@
         "node": ">=16 || 14 >=14.17"
       }
     },
+    "node_modules/mkdirp": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+      "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mkdirp": "dist/cjs/src/bin.js"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/moment": {
       "version": "2.30.1",
       "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@@ -7014,6 +7068,13 @@
         "url": "https://github.com/inikulin/parse5?sponsor=1"
       }
     },
+    "node_modules/path-browserify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/path-equal": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.2.5.tgz",
@@ -8413,11 +8474,23 @@
         }
       }
     },
+    "node_modules/ts-morph": {
+      "version": "23.0.0",
+      "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-23.0.0.tgz",
+      "integrity": "sha512-FcvFx7a9E8TUe6T3ShihXJLiJOiqyafzFKUO4aqIHDUCIvADdGNShcbc2W5PMr3LerXRv7mafvFZ9lRENxJmug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ts-morph/common": "~0.24.0",
+        "code-block-writer": "^13.0.1"
+      }
+    },
     "node_modules/ts-node": {
       "version": "10.9.2",
       "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
       "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@cspotcode/source-map-support": "^0.8.0",
         "@tsconfig/node10": "^1.0.7",
diff --git a/package.json b/package.json
index be3b08bb4..2cc179f6f 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,9 @@
     "prepack": "npm run build",
     "typegen": "cd schemas && node ../s2tQuicktypeUtil.js context ../src/context/ContextTypes.ts",
     "typegen-browser": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json context/context.schema.json api ../src/api/BrowserTypes.ts",
-    "typegen-bridging": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json api/broadcastRequest.schema.json api/findInstancesRequest.schema.json api/findInstancesResponse.schema.json api/findIntentRequest.schema.json api/findIntentResponse.schema.json api/findIntentsByContextRequest.schema.json api/findIntentsByContextResponse.schema.json api/getAppMetadataRequest.schema.json api/getAppMetadataResponse.schema.json api/openRequest.schema.json api/openResponse.schema.json api/raiseIntentRequest.schema.json api/raiseIntentResponse.schema.json api/raiseIntentResultResponse.schema.json context/context.schema.json bridging ../src/bridging/BridgingTypes.ts"
+    "typegen-bridging": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json api/broadcastRequest.schema.json api/findInstancesRequest.schema.json api/findInstancesResponse.schema.json api/findIntentRequest.schema.json api/findIntentResponse.schema.json api/findIntentsByContextRequest.schema.json api/findIntentsByContextResponse.schema.json api/getAppMetadataRequest.schema.json api/getAppMetadataResponse.schema.json api/openRequest.schema.json api/openResponse.schema.json api/raiseIntentRequest.schema.json api/raiseIntentResponse.schema.json api/raiseIntentResultResponse.schema.json context/context.schema.json bridging ../src/bridging/BridgingTypes.ts",
+    "posttypegen-browser": "npm run generate-type-predicates",
+    "generate-type-predicates": "ts-node code-generation/generate-type-predicates.ts"
   },
   "husky": {
     "hooks": {
@@ -65,6 +67,8 @@
     "rimraf": "^5.0.5",
     "rollup": "4.22.4",
     "ts-jest": "29.1.2",
+    "ts-morph": "^23.0.0",
+    "ts-node": "^10.9.2",
     "tslib": "^2.0.1",
     "typescript": "^4.0.3"
   },

From 6cf70dcac3096b6104417d613ed87516cacd3592 Mon Sep 17 00:00:00 2001
From: Giles Roadnight <10414642+Roaders@users.noreply.github.com>
Date: Tue, 15 Oct 2024 12:33:42 +0100
Subject: [PATCH 2/2] Add union types of different message classes

---
 code-generation/generate-type-predicates.ts |  16 +-
 src/api/BrowserTypes.ts                     | 805 +++++++++++++++++++-
 src/api/DesktopAgent.ts                     |   1 -
 src/context/ContextTypes.ts                 |  97 ++-
 4 files changed, 832 insertions(+), 87 deletions(-)

diff --git a/code-generation/generate-type-predicates.ts b/code-generation/generate-type-predicates.ts
index b68424339..4d548bf13 100644
--- a/code-generation/generate-type-predicates.ts
+++ b/code-generation/generate-type-predicates.ts
@@ -30,11 +30,15 @@ const matchedInterfaces = convertFunctions.map(func => {
 // write a type predicate for each matched interface
 matchedInterfaces.forEach(matched => writePredicate(matched.matchingInterface, matched.func));
 
+writeUnionType("RequestMessage", interfaces, "Request");
+writeUnionType("ResponseMessage", interfaces, "Response");
+writeUnionType("EventMessage", interfaces, "Event");
 
 function writePredicate(matchingInterface: InterfaceDeclaration, func: MethodDeclaration): void{
     const predicateName = `is${matchingInterface.getName()}`;
 
-    sourceFile.addStatements(`export function ${predicateName}(value: any): value is ${matchingInterface.getName()} {
+    sourceFile.addStatements(`
+export function ${predicateName}(value: any): value is ${matchingInterface.getName()} {
     try{
         Convert.${func.getName()}(value);
         return true;
@@ -42,7 +46,17 @@ function writePredicate(matchingInterface: InterfaceDeclaration, func: MethodDec
         return false; 
     }
 }`);
+}
+
+
+
+function writeUnionType(unionName: string, interfaces: InterfaceDeclaration[], nameEndsWith: string): void{
+    const matchingInterfaces =  interfaces
+        .map(currentInterface => currentInterface.getName())
+        .filter(interfaceName => interfaceName.length > nameEndsWith.length && interfaceName.indexOf(nameEndsWith) === interfaceName.length - nameEndsWith.length);
 
+    sourceFile.addStatements(`
+export type ${unionName} = ${matchingInterfaces.join(" | ")};`);
 }
 
 sourceFile.formatText();
diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts
index 36c6ff536..3cb8616b5 100644
--- a/src/api/BrowserTypes.ts
+++ b/src/api/BrowserTypes.ts
@@ -123,7 +123,7 @@ export interface AddContextListenerRequestMeta {
      * purposes but a Desktop Agent should make its own determination of the source of a message
      * to avoid spoofing.
      */
-    source?:   AppIdentifier;
+    source?: AppIdentifier;
     timestamp: Date;
 }
 
@@ -226,13 +226,13 @@ export interface AddContextListenerResponse {
  * Metadata for messages sent by a Desktop Agent to an App in response to an API call
  */
 export interface AddContextListenerResponseMeta {
-    requestUuid:  string;
+    requestUuid: string;
     responseUuid: string;
     /**
      * Field that represents the source application that the request being responded to was
      * received from, for debugging purposes.
      */
-    source?:   AppIdentifier;
+    source?: AppIdentifier;
     timestamp: Date;
 }
 
@@ -242,7 +242,7 @@ export interface AddContextListenerResponseMeta {
  * unsuccessful.
  */
 export interface AddContextListenerResponsePayload {
-    error?:        PurpleError;
+    error?: PurpleError;
     listenerUUID?: string;
 }
 
@@ -332,7 +332,7 @@ export interface AddEventListenerResponse {
  * unsuccessful.
  */
 export interface AddEventListenerResponsePayload {
-    error?:        ResponsePayloadError;
+    error?: ResponsePayloadError;
     listenerUUID?: string;
 }
 
@@ -417,7 +417,7 @@ export interface AddIntentListenerResponse {
  * unsuccessful.
  */
 export interface AddIntentListenerResponsePayload {
-    error?:        FluffyError;
+    error?: FluffyError;
     listenerUUID?: string;
     [property: string]: any;
 }
@@ -496,13 +496,13 @@ export interface AgentResponseMessage {
  * Metadata for messages sent by a Desktop Agent to an App in response to an API call
  */
 export interface AgentResponseMessageMeta {
-    requestUuid:  string;
+    requestUuid: string;
     responseUuid: string;
     /**
      * Field that represents the source application that the request being responded to was
      * received from, for debugging purposes.
      */
-    source?:   AppIdentifier;
+    source?: AppIdentifier;
     timestamp: Date;
 }
 
@@ -552,7 +552,7 @@ export interface AppRequestMessageMeta {
      * purposes but a Desktop Agent should make its own determination of the source of a message
      * to avoid spoofing.
      */
-    source?:   AppIdentifier;
+    source?: AppIdentifier;
     timestamp: Date;
 }
 
@@ -923,7 +923,7 @@ export interface CreatePrivateChannelResponse {
  * unsuccessful.
  */
 export interface CreatePrivateChannelResponsePayload {
-    error?:          PurpleError;
+    error?: PurpleError;
     privateChannel?: Channel;
 }
 
@@ -1343,7 +1343,7 @@ export interface Fdc3UserInterfaceResolvePayload {
      * An array of AppIntent objects defining the resolution options.
      */
     appIntents: AppIntent[];
-    context:    Context;
+    context: Context;
 }
 
 /**
@@ -1693,7 +1693,7 @@ export interface FindInstancesResponse {
  * resulted in an error and including a standardized error message.
  */
 export interface FindInstancesResponsePayload {
-    error?:          FindInstancesErrors;
+    error?: FindInstancesErrors;
     appIdentifiers?: AppMetadata[];
 }
 
@@ -1754,8 +1754,8 @@ export interface FindIntentRequest {
  * The message payload typically contains the arguments to FDC3 API functions.
  */
 export interface FindIntentRequestPayload {
-    context?:    Context;
-    intent:      string;
+    context?: Context;
+    intent: string;
     resultType?: string;
 }
 
@@ -1794,7 +1794,7 @@ export interface FindIntentResponse {
  * unsuccessful.
  */
 export interface FindIntentResponsePayload {
-    error?:     FindInstancesErrors;
+    error?: FindInstancesErrors;
     appIntent?: AppIntent;
 }
 
@@ -1829,7 +1829,7 @@ export interface FindIntentsByContextRequest {
  * The message payload typically contains the arguments to FDC3 API functions.
  */
 export interface FindIntentsByContextRequestPayload {
-    context:     Context;
+    context: Context;
     resultType?: string;
 }
 
@@ -1868,7 +1868,7 @@ export interface FindIntentsByContextResponse {
  * unsuccessful.
  */
 export interface FindIntentsByContextResponsePayload {
-    error?:      FindInstancesErrors;
+    error?: FindInstancesErrors;
     appIntents?: AppIntent[];
 }
 
@@ -1940,7 +1940,7 @@ export interface GetAppMetadataResponse {
  * unsuccessful.
  */
 export interface GetAppMetadataResponsePayload {
-    error?:       FindInstancesErrors;
+    error?: FindInstancesErrors;
     appMetadata?: AppMetadata;
 }
 
@@ -2012,7 +2012,7 @@ export interface GetCurrentChannelResponse {
  * unsuccessful.
  */
 export interface GetCurrentChannelResponsePayload {
-    error?:   ResponsePayloadError;
+    error?: ResponsePayloadError;
     channel?: Channel | null;
 }
 
@@ -2170,7 +2170,7 @@ export interface GetInfoResponse {
  * unsuccessful.
  */
 export interface GetInfoResponsePayload {
-    error?:                  ResponsePayloadError;
+    error?: ResponsePayloadError;
     implementationMetadata?: ImplementationMetadata;
 }
 
@@ -2304,7 +2304,7 @@ export interface GetOrCreateChannelResponse {
  * unsuccessful.
  */
 export interface GetOrCreateChannelResponsePayload {
-    error?:   PurpleError;
+    error?: PurpleError;
     channel?: Channel;
 }
 
@@ -2375,7 +2375,7 @@ export interface GetUserChannelsResponse {
  * unsuccessful.
  */
 export interface GetUserChannelsResponsePayload {
-    error?:        PurpleError;
+    error?: PurpleError;
     userChannels?: Channel[];
 }
 
@@ -2598,7 +2598,7 @@ export interface IntentResultRequestPayload {
      * The eventUuid value of the intentEvent that the result being sent relates to.
      */
     intentEventUuid: string;
-    intentResult:    IntentResult;
+    intentResult: IntentResult;
     /**
      * The requestUuid value of the raiseIntentRequest that the result being sent relates to.
      */
@@ -2861,7 +2861,7 @@ export interface OpenResponse {
  * unsuccessful.
  */
 export interface OpenResponsePayload {
-    error?:         OpenErrorResponsePayload;
+    error?: OpenErrorResponsePayload;
     appIdentifier?: AppIdentifier;
 }
 
@@ -2957,7 +2957,7 @@ export interface PrivateChannelAddEventListenerResponse {
  * unsuccessful.
  */
 export interface PrivateChannelAddEventListenerResponsePayload {
-    error?:        PurpleError;
+    error?: PurpleError;
     listenerUUID?: string;
     [property: string]: any;
 }
@@ -3250,7 +3250,7 @@ export interface RaiseIntentForContextRequest {
  * The message payload typically contains the arguments to FDC3 API functions.
  */
 export interface RaiseIntentForContextRequestPayload {
-    app?:    AppIdentifier;
+    app?: AppIdentifier;
     context: Context;
 }
 
@@ -3389,9 +3389,9 @@ export interface RaiseIntentRequest {
  * The message payload typically contains the arguments to FDC3 API functions.
  */
 export interface RaiseIntentRequestPayload {
-    app?:    AppIdentifier;
+    app?: AppIdentifier;
     context: Context;
-    intent:  string;
+    intent: string;
 }
 
 /**
@@ -3494,7 +3494,7 @@ export interface RaiseIntentResultResponse {
  * unsuccessful.
  */
 export interface RaiseIntentResultResponsePayload {
-    error?:        ResponsePayloadError;
+    error?: ResponsePayloadError;
     intentResult?: IntentResult;
 }
 
@@ -3532,7 +3532,7 @@ export interface WebConnectionProtocol1Hello {
  */
 export interface WebConnectionProtocol1HelloMeta {
     connectionAttemptUuid: string;
-    timestamp:             Date;
+    timestamp: Date;
 }
 
 /**
@@ -3845,7 +3845,7 @@ export interface WebConnectionProtocolMessage {
  * Metadata for this connection step message
  */
 export interface WebConnectionProtocolMessageMeta {
-    timestamp:              Date;
+    timestamp: Date;
     connectionAttemptUuid?: string;
 }
 
@@ -4558,7 +4558,7 @@ function transform(val: any, typ: any, getProps: any, key: any = '', parent: any
             const typ = typs[i];
             try {
                 return transform(val, typ, getProps);
-            } catch (_) {}
+            } catch (_) { }
         }
         return invalidValue(typs, val, key, parent);
     }
@@ -4617,9 +4617,9 @@ function transform(val: any, typ: any, getProps: any, key: any = '', parent: any
     if (Array.isArray(typ)) return transformEnum(typ, val);
     if (typeof typ === "object") {
         return typ.hasOwnProperty("unionMembers") ? transformUnion(typ.unionMembers, val)
-            : typ.hasOwnProperty("arrayItems")    ? transformArray(typ.arrayItems, val)
-            : typ.hasOwnProperty("props")         ? transformObject(getProps(typ), typ.additional, val)
-            : invalidValue(typ, val, key, parent);
+            : typ.hasOwnProperty("arrayItems") ? transformArray(typ.arrayItems, val)
+                : typ.hasOwnProperty("props") ? transformObject(getProps(typ), typ.additional, val)
+                    : invalidValue(typ, val, key, parent);
     }
     // Numbers can be parsed by Date but shouldn't be.
     if (typ === Date && typeof val !== "number") return transformDate(val);
@@ -5861,3 +5861,738 @@ const typeMap: any = {
         "WCP6Goodbye",
     ],
 };
+
+export function isAddContextListenerRequest(value: any): value is AddContextListenerRequest {
+    try {
+        Convert.addContextListenerRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAddContextListenerResponse(value: any): value is AddContextListenerResponse {
+    try {
+        Convert.addContextListenerResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAddEventListenerRequest(value: any): value is AddEventListenerRequest {
+    try {
+        Convert.addEventListenerRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAddEventListenerResponse(value: any): value is AddEventListenerResponse {
+    try {
+        Convert.addEventListenerResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAddIntentListenerRequest(value: any): value is AddIntentListenerRequest {
+    try {
+        Convert.addIntentListenerRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAddIntentListenerResponse(value: any): value is AddIntentListenerResponse {
+    try {
+        Convert.addIntentListenerResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAgentEventMessage(value: any): value is AgentEventMessage {
+    try {
+        Convert.agentEventMessageToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAgentResponseMessage(value: any): value is AgentResponseMessage {
+    try {
+        Convert.agentResponseMessageToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isAppRequestMessage(value: any): value is AppRequestMessage {
+    try {
+        Convert.appRequestMessageToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isBroadcastEvent(value: any): value is BroadcastEvent {
+    try {
+        Convert.broadcastEventToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isBroadcastRequest(value: any): value is BroadcastRequest {
+    try {
+        Convert.broadcastRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isBroadcastResponse(value: any): value is BroadcastResponse {
+    try {
+        Convert.broadcastResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isChannelChangedEvent(value: any): value is ChannelChangedEvent {
+    try {
+        Convert.channelChangedEventToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isContextListenerUnsubscribeRequest(value: any): value is ContextListenerUnsubscribeRequest {
+    try {
+        Convert.contextListenerUnsubscribeRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isContextListenerUnsubscribeResponse(value: any): value is ContextListenerUnsubscribeResponse {
+    try {
+        Convert.contextListenerUnsubscribeResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isCreatePrivateChannelRequest(value: any): value is CreatePrivateChannelRequest {
+    try {
+        Convert.createPrivateChannelRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isCreatePrivateChannelResponse(value: any): value is CreatePrivateChannelResponse {
+    try {
+        Convert.createPrivateChannelResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isEventListenerUnsubscribeRequest(value: any): value is EventListenerUnsubscribeRequest {
+    try {
+        Convert.eventListenerUnsubscribeRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isEventListenerUnsubscribeResponse(value: any): value is EventListenerUnsubscribeResponse {
+    try {
+        Convert.eventListenerUnsubscribeResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceChannels(value: any): value is Fdc3UserInterfaceChannels {
+    try {
+        Convert.fdc3UserInterfaceChannelsToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceChannelSelected(value: any): value is Fdc3UserInterfaceChannelSelected {
+    try {
+        Convert.fdc3UserInterfaceChannelSelectedToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceDrag(value: any): value is Fdc3UserInterfaceDrag {
+    try {
+        Convert.fdc3UserInterfaceDragToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceHandshake(value: any): value is Fdc3UserInterfaceHandshake {
+    try {
+        Convert.fdc3UserInterfaceHandshakeToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceHello(value: any): value is Fdc3UserInterfaceHello {
+    try {
+        Convert.fdc3UserInterfaceHelloToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceMessage(value: any): value is Fdc3UserInterfaceMessage {
+    try {
+        Convert.fdc3UserInterfaceMessageToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceResolve(value: any): value is Fdc3UserInterfaceResolve {
+    try {
+        Convert.fdc3UserInterfaceResolveToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceResolveAction(value: any): value is Fdc3UserInterfaceResolveAction {
+    try {
+        Convert.fdc3UserInterfaceResolveActionToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFdc3UserInterfaceRestyle(value: any): value is Fdc3UserInterfaceRestyle {
+    try {
+        Convert.fdc3UserInterfaceRestyleToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFindInstancesRequest(value: any): value is FindInstancesRequest {
+    try {
+        Convert.findInstancesRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFindInstancesResponse(value: any): value is FindInstancesResponse {
+    try {
+        Convert.findInstancesResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFindIntentRequest(value: any): value is FindIntentRequest {
+    try {
+        Convert.findIntentRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFindIntentResponse(value: any): value is FindIntentResponse {
+    try {
+        Convert.findIntentResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFindIntentsByContextRequest(value: any): value is FindIntentsByContextRequest {
+    try {
+        Convert.findIntentsByContextRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isFindIntentsByContextResponse(value: any): value is FindIntentsByContextResponse {
+    try {
+        Convert.findIntentsByContextResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetAppMetadataRequest(value: any): value is GetAppMetadataRequest {
+    try {
+        Convert.getAppMetadataRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetAppMetadataResponse(value: any): value is GetAppMetadataResponse {
+    try {
+        Convert.getAppMetadataResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetCurrentChannelRequest(value: any): value is GetCurrentChannelRequest {
+    try {
+        Convert.getCurrentChannelRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetCurrentChannelResponse(value: any): value is GetCurrentChannelResponse {
+    try {
+        Convert.getCurrentChannelResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetCurrentContextRequest(value: any): value is GetCurrentContextRequest {
+    try {
+        Convert.getCurrentContextRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetCurrentContextResponse(value: any): value is GetCurrentContextResponse {
+    try {
+        Convert.getCurrentContextResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetInfoRequest(value: any): value is GetInfoRequest {
+    try {
+        Convert.getInfoRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetInfoResponse(value: any): value is GetInfoResponse {
+    try {
+        Convert.getInfoResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetOrCreateChannelRequest(value: any): value is GetOrCreateChannelRequest {
+    try {
+        Convert.getOrCreateChannelRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetOrCreateChannelResponse(value: any): value is GetOrCreateChannelResponse {
+    try {
+        Convert.getOrCreateChannelResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetUserChannelsRequest(value: any): value is GetUserChannelsRequest {
+    try {
+        Convert.getUserChannelsRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isGetUserChannelsResponse(value: any): value is GetUserChannelsResponse {
+    try {
+        Convert.getUserChannelsResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isHeartbeatAcknowledgementRequest(value: any): value is HeartbeatAcknowledgementRequest {
+    try {
+        Convert.heartbeatAcknowledgementRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isHeartbeatEvent(value: any): value is HeartbeatEvent {
+    try {
+        Convert.heartbeatEventToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isIntentEvent(value: any): value is IntentEvent {
+    try {
+        Convert.intentEventToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isIntentListenerUnsubscribeRequest(value: any): value is IntentListenerUnsubscribeRequest {
+    try {
+        Convert.intentListenerUnsubscribeRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isIntentListenerUnsubscribeResponse(value: any): value is IntentListenerUnsubscribeResponse {
+    try {
+        Convert.intentListenerUnsubscribeResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isIntentResultRequest(value: any): value is IntentResultRequest {
+    try {
+        Convert.intentResultRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isIntentResultResponse(value: any): value is IntentResultResponse {
+    try {
+        Convert.intentResultResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isJoinUserChannelRequest(value: any): value is JoinUserChannelRequest {
+    try {
+        Convert.joinUserChannelRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isJoinUserChannelResponse(value: any): value is JoinUserChannelResponse {
+    try {
+        Convert.joinUserChannelResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isLeaveCurrentChannelRequest(value: any): value is LeaveCurrentChannelRequest {
+    try {
+        Convert.leaveCurrentChannelRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isLeaveCurrentChannelResponse(value: any): value is LeaveCurrentChannelResponse {
+    try {
+        Convert.leaveCurrentChannelResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isOpenRequest(value: any): value is OpenRequest {
+    try {
+        Convert.openRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isOpenResponse(value: any): value is OpenResponse {
+    try {
+        Convert.openResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelAddEventListenerRequest(value: any): value is PrivateChannelAddEventListenerRequest {
+    try {
+        Convert.privateChannelAddEventListenerRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelAddEventListenerResponse(value: any): value is PrivateChannelAddEventListenerResponse {
+    try {
+        Convert.privateChannelAddEventListenerResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelDisconnectRequest(value: any): value is PrivateChannelDisconnectRequest {
+    try {
+        Convert.privateChannelDisconnectRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelDisconnectResponse(value: any): value is PrivateChannelDisconnectResponse {
+    try {
+        Convert.privateChannelDisconnectResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelOnAddContextListenerEvent(value: any): value is PrivateChannelOnAddContextListenerEvent {
+    try {
+        Convert.privateChannelOnAddContextListenerEventToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelOnDisconnectEvent(value: any): value is PrivateChannelOnDisconnectEvent {
+    try {
+        Convert.privateChannelOnDisconnectEventToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelOnUnsubscribeEvent(value: any): value is PrivateChannelOnUnsubscribeEvent {
+    try {
+        Convert.privateChannelOnUnsubscribeEventToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelUnsubscribeEventListenerRequest(value: any): value is PrivateChannelUnsubscribeEventListenerRequest {
+    try {
+        Convert.privateChannelUnsubscribeEventListenerRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isPrivateChannelUnsubscribeEventListenerResponse(value: any): value is PrivateChannelUnsubscribeEventListenerResponse {
+    try {
+        Convert.privateChannelUnsubscribeEventListenerResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isRaiseIntentForContextRequest(value: any): value is RaiseIntentForContextRequest {
+    try {
+        Convert.raiseIntentForContextRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isRaiseIntentForContextResponse(value: any): value is RaiseIntentForContextResponse {
+    try {
+        Convert.raiseIntentForContextResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isRaiseIntentRequest(value: any): value is RaiseIntentRequest {
+    try {
+        Convert.raiseIntentRequestToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isRaiseIntentResponse(value: any): value is RaiseIntentResponse {
+    try {
+        Convert.raiseIntentResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isRaiseIntentResultResponse(value: any): value is RaiseIntentResultResponse {
+    try {
+        Convert.raiseIntentResultResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocol1Hello(value: any): value is WebConnectionProtocol1Hello {
+    try {
+        Convert.webConnectionProtocol1HelloToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocol2LoadURL(value: any): value is WebConnectionProtocol2LoadURL {
+    try {
+        Convert.webConnectionProtocol2LoadURLToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocol3Handshake(value: any): value is WebConnectionProtocol3Handshake {
+    try {
+        Convert.webConnectionProtocol3HandshakeToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocol4ValidateAppIdentity(value: any): value is WebConnectionProtocol4ValidateAppIdentity {
+    try {
+        Convert.webConnectionProtocol4ValidateAppIdentityToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocol5ValidateAppIdentityFailedResponse(value: any): value is WebConnectionProtocol5ValidateAppIdentityFailedResponse {
+    try {
+        Convert.webConnectionProtocol5ValidateAppIdentityFailedResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocol5ValidateAppIdentitySuccessResponse(value: any): value is WebConnectionProtocol5ValidateAppIdentitySuccessResponse {
+    try {
+        Convert.webConnectionProtocol5ValidateAppIdentitySuccessResponseToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocol6Goodbye(value: any): value is WebConnectionProtocol6Goodbye {
+    try {
+        Convert.webConnectionProtocol6GoodbyeToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export function isWebConnectionProtocolMessage(value: any): value is WebConnectionProtocolMessage {
+    try {
+        Convert.webConnectionProtocolMessageToJson(value);
+        return true;
+    } catch (e: any) {
+        return false;
+    }
+}
+
+export type RequestMessage = AddContextListenerRequest | AddEventListenerRequest | AddIntentListenerRequest | BroadcastRequest | ContextListenerUnsubscribeRequest | CreatePrivateChannelRequest | EventListenerUnsubscribeRequest | FindInstancesRequest | FindIntentRequest | FindIntentsByContextRequest | GetAppMetadataRequest | GetCurrentChannelRequest | GetCurrentContextRequest | GetInfoRequest | GetOrCreateChannelRequest | GetUserChannelsRequest | HeartbeatAcknowledgementRequest | IntentListenerUnsubscribeRequest | IntentResultRequest | JoinUserChannelRequest | LeaveCurrentChannelRequest | OpenRequest | PrivateChannelAddEventListenerRequest | PrivateChannelDisconnectRequest | PrivateChannelUnsubscribeEventListenerRequest | RaiseIntentForContextRequest | RaiseIntentRequest;
+
+export type ResponseMessage = AddContextListenerResponse | AddEventListenerResponse | AddIntentListenerResponse | BroadcastResponse | ContextListenerUnsubscribeResponse | CreatePrivateChannelResponse | EventListenerUnsubscribeResponse | FindInstancesResponse | FindIntentResponse | FindIntentsByContextResponse | GetAppMetadataResponse | GetCurrentChannelResponse | GetCurrentContextResponse | GetInfoResponse | GetOrCreateChannelResponse | GetUserChannelsResponse | IntentListenerUnsubscribeResponse | IntentResultResponse | JoinUserChannelResponse | LeaveCurrentChannelResponse | OpenResponse | PrivateChannelAddEventListenerResponse | PrivateChannelDisconnectResponse | PrivateChannelUnsubscribeEventListenerResponse | RaiseIntentForContextResponse | RaiseIntentResponse | RaiseIntentResultResponse | WebConnectionProtocol5ValidateAppIdentityFailedResponse | WebConnectionProtocol5ValidateAppIdentitySuccessResponse;
+
+export type EventMessage = BroadcastEvent | ChannelChangedEvent | HeartbeatEvent | IntentEvent | PrivateChannelOnAddContextListenerEvent | PrivateChannelOnDisconnectEvent | PrivateChannelOnUnsubscribeEvent;
diff --git a/src/api/DesktopAgent.ts b/src/api/DesktopAgent.ts
index 8d9edbdd9..3691ae3f6 100644
--- a/src/api/DesktopAgent.ts
+++ b/src/api/DesktopAgent.ts
@@ -13,7 +13,6 @@ import { ImplementationMetadata } from './ImplementationMetadata';
 import { PrivateChannel } from './PrivateChannel';
 import { AppIdentifier } from './AppIdentifier';
 import { AppMetadata } from './AppMetadata';
-import { DesktopAgentDetails } from './GetAgent';
 import { Intent } from '../intents/Intents';
 import { ContextType } from '../context/ContextType';
 import { EventHandler, FDC3EventTypes } from './Events';
diff --git a/src/context/ContextTypes.ts b/src/context/ContextTypes.ts
index f1b809287..8c4f68f75 100644
--- a/src/context/ContextTypes.ts
+++ b/src/context/ContextTypes.ts
@@ -207,7 +207,7 @@ export interface Chart {
 }
 
 /**
- * financial instrument that relates to the definition of this product
+ * A financial instrument that relates to the definition of this product
  *
  *
  *
@@ -235,7 +235,7 @@ export interface InstrumentElement {
      * interoperability between disparate data sources. This is especially useful when using an
      * `id` field that is not globally unique.
      */
-    market?: SearchCriteriaMarket;
+    market?: OrganizationMarket;
     /**
      * An optional human-readable name for the instrument
      */
@@ -304,7 +304,7 @@ export interface PurpleInstrumentIdentifiers {
  * interoperability between disparate data sources. This is especially useful when using an
  * `id` field that is not globally unique.
  */
-export interface SearchCriteriaMarket {
+export interface OrganizationMarket {
     /**
      * https://www.bloomberg.com/
      */
@@ -663,7 +663,7 @@ export interface ChatRoomObject {
     /**
      * Identifier(s) for the chat - currently unstandardized
      */
-    id: { [key: string]: any };
+    id: { [key: string]: string };
     /**
      * Display name for the chat room
      */
@@ -702,7 +702,7 @@ export interface ChatRoom {
     /**
      * Identifier(s) for the chat - currently unstandardized
      */
-    id: { [key: string]: any };
+    id: { [key: string]: string };
     /**
      * Display name for the chat room
      */
@@ -734,7 +734,7 @@ export interface ChatSearchCriteria {
      *
      * Empty search criteria can be supported to allow resetting of filters.
      */
-    criteria: SearchCriteria[];
+    criteria: Array<OrganizationObject | string>;
     type:     "fdc3.chat.searchCriteria";
     id?:      { [key: string]: any };
     name?:    string;
@@ -742,26 +742,23 @@ export interface ChatSearchCriteria {
 }
 
 /**
- * An individual criteria against which to match chat messages, based on an FDC3 context or
- * free-text string.
- *
- * financial instrument that relates to the definition of this product
+ * A financial instrument that relates to the definition of this product
  *
  *
  *
  * A financial instrument from any asset class.
  *
- * The contact that initiated the interaction
- *
- * A person contact that can be engaged with through email, calling, messaging, CMS, etc.
- *
  * An entity that can be used when referencing private companies and other organizations
  * where a specific instrument is not available or desired e.g. CRM and News workflows.
  *
  * It is valid to include extra properties and metadata as part of the organization payload,
  * but the minimum requirement is for at least one specified identifier to be provided.
+ *
+ * The contact that initiated the interaction
+ *
+ * A person contact that can be engaged with through email, calling, messaging, CMS, etc.
  */
-export interface SearchCriteria {
+export interface OrganizationObject {
     /**
      * Any combination of instrument identifiers can be used together to resolve ambiguity, or
      * for a better match. Not all applications will use the same instrument identifiers, which
@@ -777,9 +774,9 @@ export interface SearchCriteria {
      * fields, define a property that makes it clear what the value represents. Doing so will
      * make interpretation easier for the developers of target applications.
      *
-     * Identifiers that relate to the Contact represented by this context
-     *
      * Identifiers for the organization, at least one must be provided.
+     *
+     * Identifiers that relate to the Contact represented by this context
      */
     id: Identifiers;
     /**
@@ -787,16 +784,16 @@ export interface SearchCriteria {
      * interoperability between disparate data sources. This is especially useful when using an
      * `id` field that is not globally unique.
      */
-    market?: SearchCriteriaMarket;
+    market?: OrganizationMarket;
     /**
      * An optional human-readable name for the instrument
      *
-     * An optional human-readable name for the contact
-     *
      * An optional human-readable name of the organization
+     *
+     * An optional human-readable name for the contact
      */
     name?: string;
-    type:  SearchCriteriaType;
+    type:  TentacledInteractionType;
     [property: string]: any;
 }
 
@@ -815,9 +812,9 @@ export interface SearchCriteria {
  * fields, define a property that makes it clear what the value represents. Doing so will
  * make interpretation easier for the developers of target applications.
  *
- * Identifiers that relate to the Contact represented by this context
- *
  * Identifiers for the organization, at least one must be provided.
+ *
+ * Identifiers that relate to the Contact represented by this context
  */
 export interface Identifiers {
     /**
@@ -831,9 +828,9 @@ export interface Identifiers {
     /**
      * https://www.factset.com/
      *
-     * FactSet Permanent Identifier representing the contact
-     *
      * FactSet Permanent Identifier representing the organization
+     *
+     * FactSet Permanent Identifier representing the contact
      */
     FDS_ID?: string;
     /**
@@ -862,10 +859,6 @@ export interface Identifiers {
      * Unstandardized stock tickers
      */
     ticker?: string;
-    /**
-     * The email address for the contact
-     */
-    email?: string;
     /**
      * The Legal Entity Identifier (LEI) is a 20-character, alpha-numeric code based on the ISO
      * 17442 standard developed by the International Organization for Standardization (ISO). It
@@ -873,6 +866,10 @@ export interface Identifiers {
      * legal entities participating in financial transactions.
      */
     LEI?: string;
+    /**
+     * The email address for the contact
+     */
+    email?: string;
     [property: string]: any;
 }
 
@@ -882,7 +879,7 @@ export interface Identifiers {
  * `interactionType` SHOULD be one of `'Instant Message'`, `'Email'`, `'Call'`, or
  * `'Meeting'` although other string values are permitted.
  */
-export type SearchCriteriaType = "fdc3.instrument" | "fdc3.contact" | "fdc3.organization";
+export type TentacledInteractionType = "fdc3.instrument" | "fdc3.organization" | "fdc3.contact";
 
 /**
  * Free text to be used for a keyword search
@@ -1583,7 +1580,7 @@ export interface ProductObject {
      */
     id: { [key: string]: string };
     /**
-     * financial instrument that relates to the definition of this product
+     * A financial instrument that relates to the definition of this product
      */
     instrument?: InstrumentElement;
     /**
@@ -1860,7 +1857,7 @@ export interface Product {
      */
     id: { [key: string]: string };
     /**
-     * financial instrument that relates to the definition of this product
+     * A financial instrument that relates to the definition of this product
      */
     instrument?: InstrumentElement;
     /**
@@ -2510,9 +2507,9 @@ const typeMap: any = {
     ], "any"),
     "InstrumentElement": o([
         { json: "id", js: "id", typ: r("PurpleInstrumentIdentifiers") },
-        { json: "market", js: "market", typ: u(undefined, r("SearchCriteriaMarket")) },
+        { json: "market", js: "market", typ: u(undefined, r("OrganizationMarket")) },
         { json: "name", js: "name", typ: u(undefined, "") },
-        { json: "type", js: "type", typ: r("InstrumentType") },
+        { json: "type", js: "type", typ: r("PurpleInteractionType") },
     ], "any"),
     "PurpleInstrumentIdentifiers": o([
         { json: "BBG", js: "BBG", typ: u(undefined, "") },
@@ -2525,7 +2522,7 @@ const typeMap: any = {
         { json: "SEDOL", js: "SEDOL", typ: u(undefined, "") },
         { json: "ticker", js: "ticker", typ: u(undefined, "") },
     ], "any"),
-    "SearchCriteriaMarket": o([
+    "OrganizationMarket": o([
         { json: "BBG", js: "BBG", typ: u(undefined, "") },
         { json: "COUNTRY_ISOALPHA2", js: "COUNTRY_ISOALPHA2", typ: u(undefined, "") },
         { json: "MIC", js: "MIC", typ: u(undefined, "") },
@@ -2556,7 +2553,7 @@ const typeMap: any = {
     "ContactElement": o([
         { json: "id", js: "id", typ: r("PurpleContactIdentifiers") },
         { json: "name", js: "name", typ: u(undefined, "") },
-        { json: "type", js: "type", typ: r("ContactType") },
+        { json: "type", js: "type", typ: r("FluffyInteractionType") },
     ], "any"),
     "PurpleContactIdentifiers": o([
         { json: "email", js: "email", typ: u(undefined, "") },
@@ -2602,30 +2599,30 @@ const typeMap: any = {
         { json: "name", js: "name", typ: u(undefined, "") },
     ], "any"),
     "ChatRoomObject": o([
-        { json: "id", js: "id", typ: m("any") },
+        { json: "id", js: "id", typ: m("") },
         { json: "name", js: "name", typ: u(undefined, "") },
         { json: "providerName", js: "providerName", typ: "" },
         { json: "type", js: "type", typ: r("ChatRoomType") },
         { json: "url", js: "url", typ: u(undefined, "") },
     ], "any"),
     "ChatRoom": o([
-        { json: "id", js: "id", typ: m("any") },
+        { json: "id", js: "id", typ: m("") },
         { json: "name", js: "name", typ: u(undefined, "") },
         { json: "providerName", js: "providerName", typ: "" },
         { json: "type", js: "type", typ: r("ChatRoomType") },
         { json: "url", js: "url", typ: u(undefined, "") },
     ], "any"),
     "ChatSearchCriteria": o([
-        { json: "criteria", js: "criteria", typ: a(r("SearchCriteria")) },
+        { json: "criteria", js: "criteria", typ: a(u(r("OrganizationObject"), "")) },
         { json: "type", js: "type", typ: r("ChatSearchCriteriaType") },
         { json: "id", js: "id", typ: u(undefined, m("any")) },
         { json: "name", js: "name", typ: u(undefined, "") },
     ], "any"),
-    "SearchCriteria": o([
+    "OrganizationObject": o([
         { json: "id", js: "id", typ: r("Identifiers") },
-        { json: "market", js: "market", typ: u(undefined, r("SearchCriteriaMarket")) },
+        { json: "market", js: "market", typ: u(undefined, r("OrganizationMarket")) },
         { json: "name", js: "name", typ: u(undefined, "") },
-        { json: "type", js: "type", typ: r("SearchCriteriaType") },
+        { json: "type", js: "type", typ: r("TentacledInteractionType") },
     ], "any"),
     "Identifiers": o([
         { json: "BBG", js: "BBG", typ: u(undefined, "") },
@@ -2637,13 +2634,13 @@ const typeMap: any = {
         { json: "RIC", js: "RIC", typ: u(undefined, "") },
         { json: "SEDOL", js: "SEDOL", typ: u(undefined, "") },
         { json: "ticker", js: "ticker", typ: u(undefined, "") },
-        { json: "email", js: "email", typ: u(undefined, "") },
         { json: "LEI", js: "LEI", typ: u(undefined, "") },
+        { json: "email", js: "email", typ: u(undefined, "") },
     ], "any"),
     "Contact": o([
         { json: "id", js: "id", typ: r("FluffyContactIdentifiers") },
         { json: "name", js: "name", typ: u(undefined, "") },
-        { json: "type", js: "type", typ: r("ContactType") },
+        { json: "type", js: "type", typ: r("FluffyInteractionType") },
     ], "any"),
     "FluffyContactIdentifiers": o([
         { json: "email", js: "email", typ: u(undefined, "") },
@@ -2701,7 +2698,7 @@ const typeMap: any = {
         { json: "id", js: "id", typ: r("FluffyInstrumentIdentifiers") },
         { json: "market", js: "market", typ: u(undefined, r("PurpleMarket")) },
         { json: "name", js: "name", typ: u(undefined, "") },
-        { json: "type", js: "type", typ: r("InstrumentType") },
+        { json: "type", js: "type", typ: r("PurpleInteractionType") },
     ], "any"),
     "FluffyInstrumentIdentifiers": o([
         { json: "BBG", js: "BBG", typ: u(undefined, "") },
@@ -2805,7 +2802,7 @@ const typeMap: any = {
     "Organization": o([
         { json: "id", js: "id", typ: r("OrganizationIdentifiers") },
         { json: "name", js: "name", typ: u(undefined, "") },
-        { json: "type", js: "type", typ: r("OrganizationType") },
+        { json: "type", js: "type", typ: r("StickyInteractionType") },
     ], "any"),
     "OrganizationIdentifiers": o([
         { json: "FDS_ID", js: "FDS_ID", typ: u(undefined, "") },
@@ -2884,7 +2881,7 @@ const typeMap: any = {
     "ActionType": [
         "fdc3.action",
     ],
-    "InstrumentType": [
+    "PurpleInteractionType": [
         "fdc3.instrument",
     ],
     "TimeRangeType": [
@@ -2905,7 +2902,7 @@ const typeMap: any = {
     "ChartType": [
         "fdc3.chart",
     ],
-    "ContactType": [
+    "FluffyInteractionType": [
         "fdc3.contact",
     ],
     "ContactListType": [
@@ -2927,7 +2924,7 @@ const typeMap: any = {
     "ChatMessageType": [
         "fdc3.chat.message",
     ],
-    "SearchCriteriaType": [
+    "TentacledInteractionType": [
         "fdc3.contact",
         "fdc3.instrument",
         "fdc3.organization",
@@ -2966,7 +2963,7 @@ const typeMap: any = {
     "OrderListType": [
         "fdc3.orderList",
     ],
-    "OrganizationType": [
+    "StickyInteractionType": [
         "fdc3.organization",
     ],
     "PositionType": [