Files
pezkuwi-telemetry/packages/common/src/feed.ts
T
2018-10-01 14:10:54 +02:00

182 lines
4.8 KiB
TypeScript

import { Opaque, Maybe } from './helpers';
import { stringify, parse, Stringified } from './stringify';
import {
FeedVersion,
Address,
Latitude,
Longitude,
City,
NodeId,
NodeCount,
NodeDetails,
NodeStats,
NodeHardware,
NodeLocation,
BlockNumber,
BlockDetails,
Timestamp,
Milliseconds,
ChainLabel
} from './types';
export const Actions = {
FeedVersion : 0x00 as 0x00,
BestBlock : 0x01 as 0x01,
AddedNode : 0x02 as 0x02,
RemovedNode : 0x03 as 0x03,
LocatedNode : 0x04 as 0x04,
ImportedBlock : 0x05 as 0x05,
NodeStats : 0x06 as 0x06,
NodeHardware : 0x07 as 0x07,
TimeSync : 0x08 as 0x08,
AddedChain : 0x09 as 0x09,
RemovedChain : 0x0A as 0x0A,
SubscribedTo : 0x0B as 0x0B,
UnsubscribedFrom : 0x0C as 0x0C,
Pong : 0x0D as 0x0D,
};
export type Action = typeof Actions[keyof typeof Actions];
export type Payload = Message['payload'];
export namespace Variants {
export interface MessageBase {
action: Action;
}
export interface FeedVersionMessage extends MessageBase {
action: typeof Actions.FeedVersion;
payload: FeedVersion;
}
export interface BestBlockMessage extends MessageBase {
action: typeof Actions.BestBlock;
payload: [BlockNumber, Timestamp, Maybe<Milliseconds>];
}
export interface AddedNodeMessage extends MessageBase {
action: typeof Actions.AddedNode;
payload: [NodeId, NodeDetails, NodeStats, NodeHardware, BlockDetails, Maybe<NodeLocation>];
}
export interface RemovedNodeMessage extends MessageBase {
action: typeof Actions.RemovedNode;
payload: NodeId;
}
export interface LocatedNodeMessage extends MessageBase {
action: typeof Actions.LocatedNode;
payload: [NodeId, Latitude, Longitude, City];
}
export interface ImportedBlockMessage extends MessageBase {
action: typeof Actions.ImportedBlock;
payload: [NodeId, BlockDetails];
}
export interface NodeStatsMessage extends MessageBase {
action: typeof Actions.NodeStats;
payload: [NodeId, NodeStats];
}
export interface NodeHardwareMessage extends MessageBase {
action: typeof Actions.NodeHardware;
payload: [NodeId, NodeHardware];
}
export interface TimeSyncMessage extends MessageBase {
action: typeof Actions.TimeSync;
payload: Timestamp;
}
export interface AddedChainMessage extends MessageBase {
action: typeof Actions.AddedChain;
payload: [ChainLabel, NodeCount];
}
export interface RemovedChainMessage extends MessageBase {
action: typeof Actions.RemovedChain;
payload: ChainLabel;
}
export interface SubscribedToMessage extends MessageBase {
action: typeof Actions.SubscribedTo;
payload: ChainLabel;
}
export interface UnsubscribedFromMessage extends MessageBase {
action: typeof Actions.UnsubscribedFrom;
payload: ChainLabel;
}
export interface PongMessage extends MessageBase {
action: typeof Actions.Pong;
payload: string; // just echo whatever `ping` sent
}
}
export type Message =
| Variants.FeedVersionMessage
| Variants.BestBlockMessage
| Variants.AddedNodeMessage
| Variants.RemovedNodeMessage
| Variants.LocatedNodeMessage
| Variants.ImportedBlockMessage
| Variants.NodeStatsMessage
| Variants.NodeHardwareMessage
| Variants.TimeSyncMessage
| Variants.AddedChainMessage
| Variants.RemovedChainMessage
| Variants.SubscribedToMessage
| Variants.UnsubscribedFromMessage
| Variants.PongMessage;
/**
* Data type to be sent to the feed. Passing through strings means we can only serialize once,
* no matter how many feed clients are listening in.
*/
export interface SquashedMessages extends Array<Action | Payload> {};
export type Data = Stringified<SquashedMessages>;
/**
* Serialize an array of `Message`s to a single JSON string.
*
* All messages are squashed into a single array of alternating opcodes and payloads.
*
* Action `string`s are converted to opcodes using the `actionToCode` mapping.
*/
export function serialize(messages: Array<Message>): Data {
const squashed: SquashedMessages = new Array(messages.length * 2);
let index = 0;
messages.forEach((message) => {
const { action, payload } = message;
squashed[index++] = action;
squashed[index++] = payload;
})
return stringify(squashed);
}
/**
* Deserialize data to an array of `Message`s.
*/
export function deserialize(data: Data): Array<Message> {
const json = parse(data);
if (!Array.isArray(json) || json.length === 0 || json.length % 2 !== 0) {
throw new Error('Invalid FeedMessage.Data');
}
const messages = new Array<Message>(json.length / 2);
for (const index of messages.keys()) {
const [ action, payload ] = json.slice(index * 2);
messages[index] = { action, payload } as Message;
}
return messages;
}