/** * * Agora Real Time Engagement * Created by Wei Hu in 2021-11. * Copyright (c) 2022 Agora IO. All rights reserved. * */ import { RteLoc } from '../../common/common'; import rte_addon from '../../rte_addon'; import { Value } from '../../value/value'; import { Msg } from '../msg'; export interface CmdValue { command: string; src?: RteLoc; dest?: RteLoc[]; cmd_id?: string; seq_id?: string; [index: string]: any; } export interface CmdStatusValue extends CmdValue { command: 'status'; status_code: string; detail: string; } export function isCmdStatus( cmd: CmdValue | CmdStatusValue, ): cmd is CmdStatusValue { return cmd.command === 'status' ? true : false; } const proxy = (object: Cmd): Cmd => { return new Proxy(object, { get(target, prop, receiver): any { if (Reflect.has(target, prop)) { // Because the 'this' variable in member functions of Cmd needs to be // the proxy instance, we need to pass 'receiver' variable to Reflect. return Reflect.get(target, prop, receiver); } if (typeof prop === 'string') { const toJsonFunc = target.toJson.bind(receiver); return toJsonFunc()[prop]; } return undefined; }, has(target, key): boolean { if (!(key in target)) { const toJsonFunc = target.toJson.bind(this); return key in toJsonFunc(); } return true; }, }); }; export class Cmd extends Msg implements CmdValue { constructor(value: CmdValue | undefined = undefined) { super(); const agent = proxy(this); // TODO(Lyuge): value=undefined is only called from C, this is used to // create a pure JS cmd instance, and link with the C cmd later. // Might need to find a way so that users can only call `new Cmd({...})`. if (value !== undefined) { const jsonStr = JSON.stringify(value); if (!value.command) { throw new Error(`missing "command" field in json: ${jsonStr}`); } // Note that the wrapped instance is the 'proxy' itself, rather than the // Cmd instance. rte_addon.rte_nodejs_cmd_create(agent, jsonStr); } return agent; } getId(): string { return rte_addon.rte_nodejs_cmd_get_id(this); } setJson(value: CmdValue): void { const jsonStr = JSON.stringify(value); if (value && !value.command) { throw new Error(`missing "command" field in json: ${jsonStr}`); } rte_addon.rte_nodejs_cmd_set_json(this, jsonStr); } toJson(): CmdValue { const jsonStr = rte_addon.rte_nodejs_cmd_to_json(this); return JSON.parse(jsonStr); } readonly [index: string]: any; readonly command: string; readonly cmd_id?: string | undefined; readonly seq_id?: string | undefined; } export class StatusCmd extends Cmd { constructor( status_code: 'ok' | 'error', detail: string | Record | unknown[], ) { // TODO(Liu): Move the declaration to the common module. let detail_type = 'text/plain'; let detail_str = detail; if (typeof detail === 'object') { detail_type = 'application/json'; detail_str = JSON.stringify(detail); } super({ command: 'status', status_code, detail: detail_str, }); this.setProperty('rte_status_detail_type', new Value(detail_type)); } } rte_addon.rte_nodejs_cmd_register_class(Cmd);