// Remote Call Receiver MIXIN
// Wraps a component and exposes its public methods for remote invocation via 'remote-call' event.
// Example:
//
//
//
// Callers can then do: caller.callRemote('player','play')
import { MixinBase } from '../common/mixin-base.js';
const AttributeKeys = {
ALLOW: 'allow', // comma separated whitelist, optional
DENY: 'deny', // blacklist, optional
};
const EVENT_NAME = 'remote-call';
class RemoteCallReceiverMixin extends MixinBase() {
[key: string]: any;
static get observedAttributes() {
return ['id', ...Object.values(AttributeKeys)];
}
constructor() {
super();
this._boundHandler = this._handleRemoteCall.bind(this);
}
connectedCallback() {
super.connectedCallback && super.connectedCallback();
document.addEventListener(EVENT_NAME, this._boundHandler);
}
disconnectedCallback() {
super.disconnectedCallback && super.disconnectedCallback();
document.removeEventListener(EVENT_NAME, this._boundHandler);
}
_parseList(attr: string) {
const v = (this.getAttribute(attr) || '').trim();
return v ? v.split(',').map(s=>s.trim()).filter(Boolean) : null;
}
/** override handler to forward to inner component */
_handleRemoteCall(event: CustomEvent) {
const { targetId, method, args = [] } = event.detail || {};
if (!targetId || targetId !== this.id) {
return;
}
const allowList = this._parseList(AttributeKeys.ALLOW);
const denyList = this._parseList(AttributeKeys.DENY);
if ((method as string).startsWith('_')) {
console.error(`Cannot call private method: ${method}`);
return;
}
if (denyList && denyList.includes(method)) {
console.warn(`Method ${method} is denied on ${this.id}`);
return;
}
if (allowList && !allowList.includes(method)) {
console.warn(`Method ${method} not in allow list on ${this.id}`);
return;
}
const target = this.findActualTargetComponent();
if (!target) {
console.error('RemoteCallReceiverMixin: no inner target');
return;
}
if (typeof (target as unknown as Record)[method] !== 'function') {
console.error(`Method ${method} does not exist on wrapped component of ${this.id}`);
return;
}
try {
((target as unknown as Record)[method] as (...args: unknown[]) => unknown)(...(args as unknown[]));
} catch(err) {
console.error(`Error executing ${method} on wrapped component of ${this.id}:`, err);
}
}
}
export { RemoteCallReceiverMixin };