Files
tildefriends/core/tfrpc.js
Cory McWilliams 705e8b553f
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m35s
docs: Expose the rest of the core js to doxygen.
2025-07-27 21:48:18 -04:00

149 lines
3.0 KiB
JavaScript

/**
* \file
* \defgroup tfrpc Tilde Friends RPC.
* Tilde Friends RPC.
* @{
*/
/** Whether this module is being run in a web browser. */
const k_is_browser = get_is_browser();
/** Registered methods. */
let g_api = {};
/** The next method identifier. */
let g_next_id = 1;
/** Identifiers of pending calls. */
let g_calls = {};
/**
* TODOC
* @returns
*/
function get_is_browser() {
try {
return window !== undefined && console !== undefined;
} catch {
return false;
}
}
/** \cond */
if (k_is_browser) {
print = console.log;
}
if (k_is_browser) {
window.addEventListener('message', function (event) {
call_rpc(event.data);
});
} else {
core.register('message', function (message) {
call_rpc(message?.message);
});
}
export let rpc = new Proxy({}, {get: make_rpc});
/** \endcond */
/**
* Make a function to invoke a remote procedure.
* @param target The target.
* @param prop The name of the function.
* @param receiver The receiver.
* @return A function.
*/
function make_rpc(target, prop, receiver) {
return function () {
let id = g_next_id++;
while (!id || g_calls[id] !== undefined) {
id = g_next_id++;
}
let promise = new Promise(function (resolve, reject) {
g_calls[id] = {resolve: resolve, reject: reject};
});
if (k_is_browser) {
window.parent.postMessage(
{message: 'tfrpc', method: prop, params: [...arguments], id: id},
'*'
);
return promise;
} else {
return app
.postMessage({
message: 'tfrpc',
method: prop,
params: [...arguments],
id: id,
})
.then((x) => promise);
}
};
}
/**
* Send a response.
* @param response The response.
*/
function send(response) {
if (k_is_browser) {
window.parent.postMessage(response, '*');
} else {
app.postMessage(response);
}
}
/**
* Invoke a remote procedure.
* @param message An object describing the call.
*/
function call_rpc(message) {
if (message && message.message === 'tfrpc') {
let id = message.id;
if (message.method) {
let method = g_api[message.method];
if (method) {
try {
Promise.resolve(method(...message.params))
.then(function (result) {
send({message: 'tfrpc', id: id, result: result});
})
.catch(function (error) {
send({message: 'tfrpc', id: id, error: error});
});
} catch (error) {
send({message: 'tfrpc', id: id, error: error});
}
} else {
send({
message: 'tfrpc',
id: id,
error: `Method '${message.method}' not found.`,
});
}
} else if (message.error !== undefined) {
if (g_calls[id]) {
g_calls[id].reject(message.error);
delete g_calls[id];
} else {
throw new Error(id + ' not found to reply.');
}
} else {
if (g_calls[id]) {
g_calls[id].resolve(message.result);
delete g_calls[id];
} else {
throw new Error(id + ' not found to reply.');
}
}
}
}
/**
* Register a function that to be called remotely.
* @param method The method.
*/
export function register(method) {
g_api[method.name] = method;
}
/** @} */