diff --git a/package-lock.json b/package-lock.json index e88af96e..8d062f43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3914,14 +3914,10 @@ "license": "MIT" }, "node_modules/decoders": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/decoders/-/decoders-1.15.0.tgz", - "integrity": "sha512-KiOex7H31P2LjQgH3azmcRThaIcVGspaHIQlx6e/ik1PNUCs68QTTV8Qs6kowXCtCl4Q5yc1eGo7cbSwTqa9QA==", - "license": "MIT", - "dependencies": { - "debrief": "^1.2.6", - "lemons": "^1.4.0" - } + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/decoders/-/decoders-2.8.0.tgz", + "integrity": "sha512-VONc1zao3a/RcJLaOJSpekRku/e1a3ZDVxGj7TLYUGe0ASSFTSYXgL4Uf0mYIaQarLCxSb8TlUXcC1jBLYwVOA==", + "license": "MIT" }, "node_modules/dedent": { "version": "0.7.0", @@ -10272,7 +10268,7 @@ "version": "6.5.0", "license": "MIT", "dependencies": { - "decoders": "1.15.0" + "decoders": "^2.8.0" }, "devDependencies": { "@rollup/plugin-commonjs": "^26.0.3", diff --git a/packages/iframe-coordinator/package.json b/packages/iframe-coordinator/package.json index 98bc176a..b5c63474 100644 --- a/packages/iframe-coordinator/package.json +++ b/packages/iframe-coordinator/package.json @@ -3,7 +3,7 @@ "version": "6.5.0", "description": "Tools for coordinating embedded apps via iframes.", "dependencies": { - "decoders": "1.15.0" + "decoders": "^2.8.0" }, "type": "module", "files": [ diff --git a/packages/iframe-coordinator/src/messages/ClientToHost.ts b/packages/iframe-coordinator/src/messages/ClientToHost.ts index 59d20807..ddd2051f 100644 --- a/packages/iframe-coordinator/src/messages/ClientToHost.ts +++ b/packages/iframe-coordinator/src/messages/ClientToHost.ts @@ -1,4 +1,4 @@ -import { dispatch, guard } from "decoders"; +import { taggedUnion } from "decoders"; import { decoder as clickDecoder, LabeledClick } from "./Click"; import { decoder as keyDownDecoder, LabeledKeyDown } from "./KeyDown"; import { LabeledStarted, startedDecoder } from "./Lifecycle"; @@ -36,18 +36,16 @@ export type ClientToHost = * @param msg The message requiring validation. */ export function validate(msg: any): ClientToHost { - return guard( - dispatch("msgType", { - publish: publicationDecoder, - registeredKeyFired: keyDownDecoder, - client_started: startedDecoder, - clickFired: clickDecoder, - navRequest: navRequestDecoder, - notifyRequest: notifyDecoder, - toastRequest: notifyDecoder, - modalRequest: modalDecoder, - pageMetadata: pageMetadataDecoder, - promptOnLeave: promptDecoder, - }), - )(msg); + return taggedUnion("msgType", { + publish: publicationDecoder, + registeredKeyFired: keyDownDecoder, + client_started: startedDecoder, + clickFired: clickDecoder, + navRequest: navRequestDecoder, + notifyRequest: notifyDecoder, + toastRequest: notifyDecoder, + modalRequest: modalDecoder, + pageMetadata: pageMetadataDecoder, + promptOnLeave: promptDecoder, + }).verify(msg); } diff --git a/packages/iframe-coordinator/src/messages/HostToClient.ts b/packages/iframe-coordinator/src/messages/HostToClient.ts index 43c9ac13..069685f8 100644 --- a/packages/iframe-coordinator/src/messages/HostToClient.ts +++ b/packages/iframe-coordinator/src/messages/HostToClient.ts @@ -1,4 +1,4 @@ -import { dispatch, guard } from "decoders"; +import { taggedUnion } from "decoders"; import { envDecoder, LabeledEnvInit } from "./Lifecycle"; import { decoder as publicationDecoder, @@ -17,7 +17,8 @@ export type HostToClient = LabeledPublication | LabeledEnvInit; * @param msg The message requiring validation. */ export function validate(msg: any): HostToClient { - return guard( - dispatch("msgType", { publish: publicationDecoder, env_init: envDecoder }), - )(msg); + return taggedUnion("msgType", { + publish: publicationDecoder, + env_init: envDecoder, + }).verify(msg); } diff --git a/packages/iframe-coordinator/src/messages/LabeledMsg.ts b/packages/iframe-coordinator/src/messages/LabeledMsg.ts index ff8783d9..4b2beb55 100644 --- a/packages/iframe-coordinator/src/messages/LabeledMsg.ts +++ b/packages/iframe-coordinator/src/messages/LabeledMsg.ts @@ -1,13 +1,4 @@ -import { - constant, - Decoder, - either, - hardcoded, - map, - object, - optional, - string, -} from "decoders"; +import { constant, Decoder, either, exact, optional, string } from "decoders"; // This is replaced by rollup-plugin-replace with the actual version from package.json const version = "__PACKAGE_VERSION__"; @@ -87,20 +78,21 @@ export function labeledDecoder( typeDecoder: Decoder, msgDecoder: Decoder, ): Decoder> { - return object({ + return exact({ // TODO: in 4.0.0 make protocol and version fields mandatory - protocol: either( - constant<"iframe-coordinator">(API_PROTOCOL), - hardcoded<"iframe-coordinator">(API_PROTOCOL), - ), - version: either(string, hardcoded("unknown")), + protocol: optional(constant<"iframe-coordinator">(API_PROTOCOL)), + version: optional(string), msgType: typeDecoder, msg: msgDecoder, direction: optional(directionDecoder), - }); + }).transform((decoded) => ({ + ...decoded, + protocol: decoded.protocol ?? API_PROTOCOL, + version: decoded.version ?? "unknown", + })) as Decoder>; } -const directionDecoder: Decoder = either( +const directionDecoder: Decoder = either( constant("ClientToHost"), constant("HostToClient"), ); diff --git a/packages/iframe-coordinator/src/messages/Lifecycle.ts b/packages/iframe-coordinator/src/messages/Lifecycle.ts index 05eddb4c..1e31716c 100644 --- a/packages/iframe-coordinator/src/messages/Lifecycle.ts +++ b/packages/iframe-coordinator/src/messages/Lifecycle.ts @@ -3,10 +3,10 @@ import { boolean, constant, Decoder, - mixed, - object, + exact, optional, string, + unknown, } from "decoders"; import { applyClientProtocol, labeledDecoder, LabeledMsg } from "./LabeledMsg"; @@ -23,7 +23,7 @@ export interface LabeledStarted extends LabeledMsg<"client_started", any> { // We don't care what is in msg for Started messages. const startedDecoder: Decoder = labeledDecoder( constant<"client_started">("client_started"), - mixed, + unknown, ); /** @@ -78,13 +78,13 @@ export interface LabeledEnvInit extends LabeledMsg<"env_init", SetupData> { const envDecoder: Decoder = labeledDecoder( constant<"env_init">("env_init"), - object({ + exact({ locale: string, hostRootUrl: string, assignedRoute: string, registeredKeys: optional( array( - object({ + exact({ key: string, altKey: optional(boolean), ctrlKey: optional(boolean), @@ -93,7 +93,7 @@ const envDecoder: Decoder = labeledDecoder( }), ), ), - custom: mixed, + custom: unknown, }), ); diff --git a/packages/iframe-coordinator/src/messages/ModalRequest.ts b/packages/iframe-coordinator/src/messages/ModalRequest.ts index e9c3b7ff..f51fa8c4 100644 --- a/packages/iframe-coordinator/src/messages/ModalRequest.ts +++ b/packages/iframe-coordinator/src/messages/ModalRequest.ts @@ -1,4 +1,4 @@ -import { constant, Decoder, mixed, object, string } from "decoders"; +import { constant, Decoder, exact, string, unknown } from "decoders"; import { labeledDecoder, LabeledMsg } from "./LabeledMsg"; /** @@ -9,7 +9,7 @@ export interface ModalRequest { modalId: string; /** Any data that the client wishes to send to the modal */ - modalData: any; + modalData?: any; } /** @@ -28,9 +28,9 @@ export interface LabeledModalRequest extends LabeledMsg< const decoder: Decoder = labeledDecoder( constant<"modalRequest">("modalRequest"), - object({ + exact({ modalId: string, - modalData: mixed, + modalData: unknown, }), ); diff --git a/packages/iframe-coordinator/src/messages/Notification.ts b/packages/iframe-coordinator/src/messages/Notification.ts index 3e87c041..ffc053ef 100644 --- a/packages/iframe-coordinator/src/messages/Notification.ts +++ b/packages/iframe-coordinator/src/messages/Notification.ts @@ -2,11 +2,10 @@ import { constant, Decoder, either, - map, - mixed, - object, + exact, optional, string, + unknown, } from "decoders"; import { labeledDecoder, LabeledMsg } from "./LabeledMsg"; @@ -20,7 +19,7 @@ export interface Notification { /** The notification message */ message: string; /** Additional host-specific options such as severity */ - custom: any; + custom?: any; } /** @@ -40,21 +39,19 @@ export interface LabeledNotification extends LabeledMsg< /** * Helper function to convert old message types to the new type */ -function alwaysMsgType(msgType: "string"): "notifyRequest" { +function alwaysMsgType(msgType: "toastRequest"): "notifyRequest" { return "notifyRequest"; } -const toastTypeDecoder: Decoder<"notifyRequest"> = map( - constant<"toastRequest">("toastRequest"), - alwaysMsgType, -); +const toastTypeDecoder: Decoder<"notifyRequest"> = + constant<"toastRequest">("toastRequest").transform(alwaysMsgType); const decoder: Decoder = labeledDecoder( either(constant<"notifyRequest">("notifyRequest"), toastTypeDecoder), - object({ + exact({ title: optional(string), message: string, - custom: mixed, + custom: unknown, }), ); diff --git a/packages/iframe-coordinator/src/messages/PageMetadata.ts b/packages/iframe-coordinator/src/messages/PageMetadata.ts index a149b263..5271f5a2 100644 --- a/packages/iframe-coordinator/src/messages/PageMetadata.ts +++ b/packages/iframe-coordinator/src/messages/PageMetadata.ts @@ -2,10 +2,10 @@ import { array, constant, Decoder, - mixed, - object, + exact, optional, string, + unknown, } from "decoders"; import { labeledDecoder, LabeledMsg } from "./LabeledMsg"; @@ -47,15 +47,15 @@ export interface LabeledPageMetadata extends LabeledMsg< const decoder: Decoder = labeledDecoder( constant<"pageMetadata">("pageMetadata"), - object({ + exact({ title: string, breadcrumbs: array( - object({ + exact({ text: string, href: string, }), ), - custom: optional(mixed), + custom: optional(unknown), }), ); diff --git a/packages/iframe-coordinator/src/messages/Publication.ts b/packages/iframe-coordinator/src/messages/Publication.ts index 5acc6301..eff0026c 100644 --- a/packages/iframe-coordinator/src/messages/Publication.ts +++ b/packages/iframe-coordinator/src/messages/Publication.ts @@ -1,12 +1,4 @@ -import { - constant, - Decoder, - map, - mixed, - object, - optional, - string, -} from "decoders"; +import { constant, Decoder, exact, optional, string, unknown } from "decoders"; import { labeledDecoder, LabeledMsg } from "./LabeledMsg"; /** @@ -19,7 +11,7 @@ export interface Publication { */ topic: string; /** Data to publish */ - payload: any; + payload?: any; /** * Client the message originates from. This should not be provided when * calling client methods. The value will be ignored and the library @@ -41,9 +33,9 @@ export interface LabeledPublication extends LabeledMsg<"publish", Publication> { const decoder: Decoder = labeledDecoder( constant<"publish">("publish"), - object({ + exact({ topic: string, - payload: mixed, + payload: unknown, clientId: optional(string), }), );