Skip to content

Commit

Permalink
Create schema
Browse files Browse the repository at this point in the history
  • Loading branch information
chekrd committed Nov 26, 2024
1 parent cecd864 commit 7bf258a
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 67 deletions.
5 changes: 3 additions & 2 deletions src/customAppSdk.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isType } from './isType';
import { sendMessage, startListening } from './iframeMessenger';
import { ErrorMessage } from './iframeSchema';
import { isType } from './isType';

export enum ErrorCode {
Nnn = 'Nnn',
Expand Down Expand Up @@ -33,13 +33,14 @@ export const initCustomApp = (): Promise<InitReturn> => {

return new Promise((resolve, reject) => {
try {
sendMessage<'init-request', '1.0.0'>(
sendMessage<'init@1.0.0'>(
{
type: 'init-request',
version: '1.0.0',
payload: null,
},
(response) => {

if (isType(ErrorMessage, response)) {
resolve({ isError: true, code: response.code, description: response.description });
} else {
Expand Down
23 changes: 9 additions & 14 deletions src/iframeMessenger.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
import { createUuid } from './createUuid';
import type { CustomAppMessage } from './iframeSchema';
import type { GetRequestModelFor, GetResponseModelFor } from './utilityTypes';
import type { Schema } from './iframeSchema';
import type { AllIncomingMessages } from './utilityTypes';

let callbacks: Readonly<Record<string, (data: CustomAppMessage[1]) => void>> = {};
let callbacks: Readonly<Record<string, (data: AllIncomingMessages) => void>> = {};

export const startListening = () => {
export const startListening = ():void => {
if (window.self === window.top) {
throw new Error('Custom app is not hosted in an IFrame.');
}

window.addEventListener('message', processMessage, true);
};

export const sendMessage = <
TType extends CustomAppMessage[0]['type'],
TVersion extends CustomAppMessage[0]['version'],
>(
message: Omit<GetRequestModelFor<TType, TVersion>, 'requestId'>,
callback: (data: GetResponseModelFor<TType, TVersion>) => void,
export const sendMessage = <TMessageType extends keyof Schema['client']>(
message: Omit<Schema['client'][TMessageType]['request'], 'requestId'>,
callback: (data: Schema['client'][TMessageType]['response']) => void,
): void => {
const requestId = createUuid();
callbacks = { ...callbacks, [requestId]: callback } as Readonly<
Record<string, (data: CustomAppMessage[1]) => void>
>;
callbacks = { ...callbacks, [requestId]: callback } as typeof callbacks;
window.parent.postMessage({ ...message, requestId }, '*');
};

const processMessage = (event: MessageEvent<CustomAppMessage[1]>): void => {
const processMessage = (event: MessageEvent<AllIncomingMessages>): void => {
const message = event.data;
const callback = callbacks[message.requestId];
callbacks = Object.fromEntries(
Expand Down
47 changes: 15 additions & 32 deletions src/iframeSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ export const ErrorMessage = z
})
.readonly();

type ErrorMessage = z.infer<typeof ErrorMessage>;

const ClientInitV1Request = z
.object({
type: z.literal('init-request'),
Expand All @@ -24,8 +22,6 @@ const ClientInitV1Request = z
})
.readonly();

type ClientInitV1Request = z.infer<typeof ClientInitV1Request>;

const ClientInitV1Response = z
.object({
type: z.literal('init-response'),
Expand Down Expand Up @@ -54,10 +50,9 @@ const ClientInitV1Response = z
requestId: z.string().uuid(),
version: z.literal('1.0.0'),
})
.or(ErrorMessage)
.readonly();

type ClientInitV1Response = z.infer<typeof ClientInitV1Response>;

const ClientInitV2Request = z
.object({
type: z.literal('init-request'),
Expand All @@ -67,8 +62,6 @@ const ClientInitV2Request = z
})
.readonly();

type ClientInitV2Request = z.infer<typeof ClientInitV2Request>;

const ClientInitV2Response = z
.object({
type: z.literal('init-response'),
Expand All @@ -81,10 +74,9 @@ const ClientInitV2Response = z
requestId: z.string().uuid(),
version: z.literal('2.0.0'),
})
.or(ErrorMessage)
.readonly();

type ClientInitV2Response = z.infer<typeof ClientInitV2Response>;

const GetV1Request = z
.object({
type: z.literal('get-request'),
Expand All @@ -94,50 +86,41 @@ const GetV1Request = z
})
.readonly();

type GetV1Request = z.infer<typeof GetV1Request>;

const GetV1Response = z
.object({
type: z.literal('get-response'),
requestId: z.string().uuid(),
version: z.literal('1.0.0'),
payload: z.null(),
})
.or(ErrorMessage)
.readonly();

type GetV1Response = z.infer<typeof GetV1Response>;

const HostPingV1Request = z.object({
type: z.literal('ping-request'),
const HostItemDeletedRequest = z.object({
type: z.literal('item-deleted-request'),
requestId: z.string().uuid(),
version: z.literal('1.0.0'),
payload: z.boolean(),
payload: z.object({ itemId: z.string().uuid() }).readonly(),
});

type HostPingV1Request = z.infer<typeof HostPingV1Request>;

export type CustomAppMessage =
| [ClientInitV1Request, ClientInitV1Response | ErrorMessage]
| [ClientInitV2Request, ClientInitV2Response | ErrorMessage]
| [GetV1Request, GetV1Response | ErrorMessage];

export type Schema = {
client: {
'[email protected]': {
request: ClientInitV1Request;
response: ClientInitV1Response;
request: z.infer<typeof ClientInitV1Request>;
response: z.infer<typeof ClientInitV1Response>;
};
'[email protected]': {
request: ClientInitV2Request;
response: ClientInitV2Response;
request: z.infer<typeof ClientInitV2Request>;
response: z.infer<typeof ClientInitV2Response>;
};
'[email protected]': {
request: GetV1Request;
response: GetV1Response;
request: z.infer<typeof GetV1Request>;
response: z.infer<typeof GetV1Response>;
};
};
host: {
'ping@1.0.0': {
request: HostPingV1Request;
'item-deleted@1.0.0': {
request: z.infer<typeof HostItemDeletedRequest>;
response: null;
};
};
Expand Down
24 changes: 5 additions & 19 deletions src/utilityTypes.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
import type { CustomAppMessage } from './iframeSchema';
import type { Schema } from './iframeSchema';

export type GetRequestModelFor<
TType extends CustomAppMessage[0]['type'],
TVersion extends CustomAppMessage[0]['version'],
> = CustomAppMessage[0] extends infer TMessage
? TMessage extends { readonly type: TType; readonly version: TVersion }
? TMessage
: never
: never;
export type AllClientResponses = Schema['client'][keyof Schema['client']]['response'];

export type GetResponseModelFor<
TType extends CustomAppMessage[0]['type'],
TVersion extends CustomAppMessage[0]['version'],
> = CustomAppMessage extends infer TPair
? TPair extends [unknown, unknown]
? TPair[0] extends { readonly type: TType; readonly version: TVersion }
? TPair[1]
: never
: never
: never;
export type AllHostRequests = Schema['host'][keyof Schema['host']]['request'];

export type AllIncomingMessages = AllClientResponses | AllHostRequests;

0 comments on commit 7bf258a

Please sign in to comment.