Newer
Older
import { extractCommand, makeBuffer, search, searchParent } from "./util";
import {
CommandType,
CallFn,
CommandObject,
PbRequestMessage,
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";
const commands: Record<USPVersion, Record<CommandType, CommandObject>> = {
"1.0": v10,
"1.1": v10,
"1.2": v12,
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 readMsg = (proto: Proto, data: any): Record<string, any> => {
const record = proto.rootRecord.lookupType("usp_record.Record");
const decodedRecord: any = record.decode(
"binaryData" in data ? data.binaryData : data
);
const msg = proto.rootMsg.lookupType("usp.Msg");
const decodedMsg = msg.decode(decodedRecord.noSessionContext.payload);
return JSON.parse(JSON.stringify(decodedMsg)); // forces conversions
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 = commands[version][command] || null;
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 =
commands[version][cmdType as CommandType] || null;
if (options.raw) return parsedMsg;
const [decodedData] = cmd.decode(parsedMsg, options);
return decodedData;
export const makeEncode =
(proto: Proto, options?: Record<string, string>) =>
(
command: CommandType,
args: Record<string, any>
): [string, any, string | null] => {
const cmd = commands[version][command] || null;
if (!cmd) return ["error", null, `Uknown command: ${command}`];
return [...convert(proto, cmd.encode(args), options)];
};
const convert = (
msg: PbRequestMessage,
bufferOptions?: Record<string, string>
): [string, any, string | null] => {
const id = msg.header.msgId;
msg.header.msgType = proto.header.MsgType[msg.header.msgType];
if (isError(converted)) return [id, null, converted];
const encoded = proto.rootMsg
.lookupType("usp.Msg")
.encode(converted)
.finish();
const buffer = makeBuffer(proto.rootRecord, encoded, bufferOptions || {});
return [id, buffer, null];
};
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;
};