Newer
Older
import { extractCommand, makeBuffer, search, searchParent } from "./util";
import {
CommandType,
CallFn,
RecipeObject,
OnFn,
DecodeResponse,
} from "../types";
import resolve from "./recipes/resolve";
import operateRecipe from "./recipes/operate";
import subscribe from "./recipes/subscribe";
import v10 from "./1.0,1.1";
import v12 from "./1.2";
import common from "./common";
// Note: version files are empty for now, they used to be necessary but were later made obsolete. I still keep them around in case version specific changes crop up again.
const getCommand = (
version: USPVersion,
commandType: CommandType
): CommandObject | null =>
version in versionSpecificCommands &&
commandType in versionSpecificCommands[version]
? versionSpecificCommands[version][commandType]
: common[commandType] || null;
const recipes: RecipeObject[] = [resolve as any, operateRecipe, subscribe];
export const makeRecipes = (call: CallFn, on: OnFn): any =>
recipes.reduce(
(acc, { make, name }) => ({ ...acc, [name]: make(call, on) }),
{}
);
export const decodeId = (data: any) => String(data);
Marin Karamihalev
committed
const unkownErr = (
msg: Record<string, string>
): [string, string, Record<string, any>] => ["error", "", msg];
export const decodeRecord = (proto: Proto, data: any): Record<string, any> => {
const record = proto.rootRecord.lookupType("usp_record.Record");
const decodedRecord = record.decode(
"binaryData" in data ? data.binaryData : data
);
return JSON.parse(JSON.stringify(decodedRecord));
};
Marin Karamihalev
committed
export const readMsg = (
proto: Proto,
data: any
): [Record<string, any>, unknown, boolean] => {
const record = proto.rootRecord.lookupType("usp_record.Record");
const decodedRecord = record.decode(
"binaryData" in data ? data.binaryData : data
);
Marin Karamihalev
committed
const convertedDecodedRecord = JSON.parse(JSON.stringify(decodedRecord))
if ("disconnect" in convertedDecodedRecord)
return [{}, convertedDecodedRecord.disconnect, true];
try {
const msg = proto.rootMsg.lookupType("usp.Msg");
const decodedMsg = msg.decode(
decodedRecord.noSessionContext?.payload ||
decodedRecord.sessionContext?.payload[0]
);
// forces conversion
return [JSON.parse(JSON.stringify(decodedMsg)), null, false];
} catch (err) {
return [{}, err, false];
}
type DecodeFn = (parsedMsg: any, version: USPVersion) => DecodeResponse;
export const decode: DecodeFn = (parsedMsg, version: USPVersion) => {
const err = searchParent(parsedMsg, "errMsg") || null;
const command = extractCommand(parsedMsg);
const foundId = search(parsedMsg, "msgId");
// if id is formatted by me (command@) then use, otherwise check for sub id
? foundId
: search(parsedMsg, "subscriptionId") || null;
// if command is unkown
if (!command) return unkownErr(parsedMsg);
if (err) return [id, null, err, command];
const cmd: CommandObject | null = getCommand(version, command);
if (!cmd) return unkownErr(parsedMsg);
const [decodedData, decodedId, decodedErr] = cmd.decode(parsedMsg);
return [decodedId || id, decodedData, decodedErr || err, command];
};
parsedMsg: any,
cmdType: CommandType | undefined,
options: DecodeOptions,
version: USPVersion
) => any;
export const decodeWithOptions: DecodeWithOptionsFn = (
parsedMsg,
cmdType,
options,
version: USPVersion
const cmd: CommandObject | null = getCommand(version, cmdType as CommandType);
if (options.raw) return parsedMsg;
const [decodedData] = cmd.decode(parsedMsg, options);
return decodedData;
export const makeEmptySessionMessage = (
proto: Proto,
bufferOptions: Record<string, string>,
sessionOptions: USPSession,
version: USPVersion,
useSession: boolean
) => {
const converted = _convert(proto, {});
if (isError(converted)) return null;
const encoded = proto.rootMsg
.lookupType("usp.Msg")
.encode(converted)
.finish();
const buffer = makeBuffer(
proto.rootRecord,
encoded,
version,
bufferOptions,
sessionOptions,
useSession
);
return buffer;
};
export const makeEncode =
(proto: Proto, bufferOptions: Record<string, string>, useSession: boolean) =>
(
command: CommandType,
args: Record<string, any>,
sessionOptions: USPSession
): [string, any, string | null] => {
if (!cmd) return ["error", null, `Uknown command: ${command}`];
const msg = cmd.encode(args);
const id = msg.header.msgId;
msg.header.msgType = proto.header.MsgType[msg.header.msgType];
const converted = _convert(proto, msg);
if (isError(converted)) return [id, null, converted];
const encoded = proto.rootMsg
.lookupType("usp.Msg")
.encode(converted)
.finish();
const buffer = makeBuffer(
proto.rootRecord,
encoded,
version,
bufferOptions,
sessionOptions,
useSession
);
return [id, buffer, null];
};
export const makeSession = (sessionId: number | null): USPSession => ({
Marin Karamihalev
committed
sessionId: sessionId || 1,
sequenceId: 1,
expectedId: 1,
export const hasSessionDisconnectError = (msg: Record<string, any>) =>
msg?.disconnect?.reasonCode === 7105;
export const msgLacksPayload = (msg: Record<string, any>) =>
msg?.sessionContext?.payload[0] === "";
const isError = (o: any): o is string => typeof o == "string";
const internalKeys = ["lookup"];
const isInternal = (key: string) => internalKeys.includes(key);
const makePayload = (items: [string, any][], isArr: boolean) =>
items
.filter(([k]) => !isInternal(k))
.reduce(
(acc: any, [k, v]) =>
isArr
? [...acc, v]
: {
...acc,
[k]: v,
},
isArr ? [] : {}
);
const isStringArray = (obj: any) =>
Array.isArray(obj) && obj.every((v) => typeof v === "string");
const needsConversion = (v: any) => typeof v === "object" && !isStringArray(v);
const _convert = (proto: Proto, value: any): string | any => {
const skip = value.lookup === undefined;
const lookup = "usp." + value.lookup;
const item = skip ? null : proto.rootMsg.lookupType(lookup);
const simpleValues: any[] = Object.entries(value).filter(
([, v]) => !needsConversion(v)
);
const toConvert = Object.entries(value).filter(([, v]) => needsConversion(v));
const converted: [string, any][] = toConvert.map(([k, v]) => [
k,
]);
const err = converted.find(([, v]) => isError(v));
if (err) return err[1];
const total = converted.concat(simpleValues);
const payload = makePayload(total, Array.isArray(value));
const payloadErr = item?.verify(payload);
if (payloadErr) return payloadErr;
return item ? item.create(payload) : payload;
};