1/** 2 * The channel id is defined as: 3 * 4 * ${scope}/${namespace}/${path} 5 * 6 * The scope drives how the namespace is used and controlled 7 * 8 * @alpha 9 */ 10export enum LiveChannelScope { 11 DataSource = 'ds', // namespace = data source ID 12 Plugin = 'plugin', // namespace = plugin name (singleton works for apps too) 13 Grafana = 'grafana', // namespace = feature 14 Stream = 'stream', // namespace = id for the managed data stream 15} 16 17/** 18 * The type of data to expect in a given channel 19 * 20 * @alpha 21 */ 22export enum LiveChannelType { 23 DataStream = 'stream', // each message contains a batch of rows that will be appened to previous values 24 DataFrame = 'frame', // each message is an entire data frame and should *replace* previous content 25 JSON = 'json', // arbitray json message 26} 27 28export enum LiveChannelConnectionState { 29 /** The connection is not yet established */ 30 Pending = 'pending', 31 /** Connected to the channel */ 32 Connected = 'connected', 33 /** Disconnected from the channel. The channel will reconnect when possible */ 34 Disconnected = 'disconnected', 35 /** Was at some point connected, and will not try to reconnect */ 36 Shutdown = 'shutdown', 37 /** Channel configuraiton was invalid and will not connect */ 38 Invalid = 'invalid', 39} 40 41export enum LiveChannelEventType { 42 Status = 'status', 43 Join = 'join', 44 Leave = 'leave', 45 Message = 'message', 46} 47 48/** 49 * @alpha -- experimental 50 */ 51export interface LiveChannelStatusEvent { 52 type: LiveChannelEventType.Status; 53 54 /** 55 * {scope}/{namespace}/{path} 56 */ 57 id: string; 58 59 /** 60 * unix millies timestamp for the last status change 61 */ 62 timestamp: number; 63 64 /** 65 * flag if the channel is actively connected to the channel. 66 * This may be false while the connections get established or if the network is lost 67 * As long as the `shutdown` flag is not set, the connection will try to reestablish 68 */ 69 state: LiveChannelConnectionState; 70 71 /** 72 * When joining a channel, there may be an initial packet in the subscribe method 73 */ 74 message?: any; 75 76 /** 77 * The last error. 78 * 79 * This will remain in the status until a new message is successfully received from the channel 80 */ 81 error?: any; 82} 83 84export interface LiveChannelJoinEvent { 85 type: LiveChannelEventType.Join; 86 user: any; // @alpha -- experimental -- will be filled in when we improve the UI 87} 88 89export interface LiveChannelLeaveEvent { 90 type: LiveChannelEventType.Leave; 91 user: any; // @alpha -- experimental -- will be filled in when we improve the UI 92} 93 94export interface LiveChannelMessageEvent<T> { 95 type: LiveChannelEventType.Message; 96 message: T; 97} 98 99export type LiveChannelEvent<T = any> = 100 | LiveChannelStatusEvent 101 | LiveChannelJoinEvent 102 | LiveChannelLeaveEvent 103 | LiveChannelMessageEvent<T>; 104 105export function isLiveChannelStatusEvent<T>(evt: LiveChannelEvent<T>): evt is LiveChannelStatusEvent { 106 return evt.type === LiveChannelEventType.Status; 107} 108 109export function isLiveChannelJoinEvent<T>(evt: LiveChannelEvent<T>): evt is LiveChannelJoinEvent { 110 return evt.type === LiveChannelEventType.Join; 111} 112 113export function isLiveChannelLeaveEvent<T>(evt: LiveChannelEvent<T>): evt is LiveChannelLeaveEvent { 114 return evt.type === LiveChannelEventType.Leave; 115} 116 117export function isLiveChannelMessageEvent<T>(evt: LiveChannelEvent<T>): evt is LiveChannelMessageEvent<T> { 118 return evt.type === LiveChannelEventType.Message; 119} 120 121/** 122 * @alpha -- experimental 123 */ 124export interface LiveChannelPresenceStatus { 125 users: any; // @alpha -- experimental -- will be filled in when we improve the UI 126} 127 128/** 129 * @alpha -- experimental 130 */ 131export interface LiveChannelAddress { 132 scope: LiveChannelScope; 133 namespace: string; // depends on the scope 134 path: string; 135} 136 137/** 138 * Return an address from a string 139 * 140 * @alpha -- experimental 141 */ 142export function parseLiveChannelAddress(id?: string): LiveChannelAddress | undefined { 143 if (id?.length) { 144 let parts = id.trim().split('/'); 145 if (parts.length >= 3) { 146 return { 147 scope: parts[0] as LiveChannelScope, 148 namespace: parts[1], 149 path: parts.slice(2).join('/'), 150 }; 151 } 152 } 153 return undefined; 154} 155 156/** 157 * Check if the address has a scope, namespace, and path 158 * 159 * @alpha -- experimental 160 */ 161export function isValidLiveChannelAddress(addr?: LiveChannelAddress): addr is LiveChannelAddress { 162 return !!(addr?.path && addr.namespace && addr.scope); 163} 164 165/** 166 * Convert the address to an explicit channel path 167 * 168 * @alpha -- experimental 169 */ 170export function toLiveChannelId(addr: LiveChannelAddress): string { 171 if (!addr.scope) { 172 return ''; 173 } 174 let id = addr.scope as string; 175 if (!addr.namespace) { 176 return id; 177 } 178 id += '/' + addr.namespace; 179 if (!addr.path) { 180 return id; 181 } 182 return id + '/' + addr.path; 183} 184