figwire
Version:
Bidirectional IPC communication between UI and core in Figma plugins. Lightweight and typed.
123 lines (117 loc) • 3.99 kB
JavaScript
var __defProp$1 = Object.defineProperty;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
var _a;
_a = Symbol.toStringTag;
class DeferredPromise {
constructor() {
__publicField$1(this, _a, "Promise");
__publicField$1(this, "_promise");
__publicField$1(this, "resolve", () => void 0);
__publicField$1(this, "reject", () => void 0);
this._promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
// biome-ignore lint/suspicious/noThenProperty: this class overwrites Promise.
then(onFulfilled, onRejected) {
return this._promise.then(onFulfilled, onRejected);
}
catch(onRejected) {
return this._promise.catch(onRejected);
}
finally(onFinally) {
return this._promise.finally(onFinally);
}
}
function hash() {
let count = 0;
return {
create() {
return ++count;
}
};
}
const isRequestMessage = (message) => message.type === "request";
const isResponseMessage = (message) => message.type === "response";
const isErrorMessage = (message) => message.type === "error";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
class API {
constructor(name, postFn, receiveFn, options = { silentError: false }) {
this.name = name;
this.postFn = postFn;
this.receiveFn = receiveFn;
this.options = options;
__publicField(this, "requestQueue", /* @__PURE__ */ new Map());
__publicField(this, "callbacks", /* @__PURE__ */ new Map());
__publicField(this, "hasher", hash());
this.receiveFn(async (message) => {
if (isRequestMessage(message)) {
const methodName = message.name.split("")[0];
const method = this.callbacks.get(methodName);
if (!method) {
const message2 = `Method "${methodName}" is not defined on "${this.name}" API. (Maybe typo?)`;
if (!this.options.silentError) {
throw new Error(message2);
}
console.error(message2);
return;
}
try {
const result = await Promise.resolve(method(...message.args));
this.postFn({
type: "response",
name: message.name,
return: result
});
} catch (error) {
if (error instanceof Error) {
this.postFn({
type: "error",
name: message.name,
message: error.message
});
}
}
}
if (isErrorMessage(message)) {
const promise = this.requestQueue.get(message.name);
if (!promise) return;
promise.reject(message.message);
this.requestQueue.delete(message.name);
}
if (isResponseMessage(message)) {
const promise = this.requestQueue.get(message.name);
if (!promise) return;
promise.resolve(message.return);
this.requestQueue.delete(message.name);
}
});
}
async request(name, args) {
const hash2 = this.hasher.create();
const requestName = `${name}${hash2}`;
const promise = new DeferredPromise();
this.requestQueue.set(requestName, promise);
this.postFn({
type: "request",
name: requestName,
args: args || []
});
return promise;
}
registerMethod(name, callback) {
this.callbacks.set(name, callback);
}
}
function createClient(api) {
return new Proxy({}, {
get(_, prop) {
return async (...args) => api.request(prop, args);
}
});
}
export { API as A, createClient as c };