refactor: relation worker use MessageHelper

This commit is contained in:
windingwind 2024-11-09 12:15:02 +01:00
parent ba4ffb03db
commit 59ab1413ad
5 changed files with 100 additions and 181 deletions

View File

@ -6,7 +6,8 @@ import hooks from "./hooks";
import api from "./api";
import { createZToolkit } from "./utils/ztoolkit";
import { MessageHelper } from "zotero-plugin-toolkit/dist/helpers/message";
import type { handlers } from "./extras/parsingWorker";
import type { handlers as parsingHandlers } from "./extras/parsingWorker";
import type { handlers as relationHandlers } from "./extras/relationWorker";
class Addon {
public data: {
@ -71,9 +72,10 @@ class Addon {
};
relation: {
worker?: Worker;
server?: MessageHelper<typeof relationHandlers>;
};
parsing: {
server?: MessageHelper<typeof handlers>;
server?: MessageHelper<typeof parsingHandlers>;
};
imageCache: Record<number, string>;
hint: {

View File

@ -1,4 +1,7 @@
import Dexie from "dexie";
import { MessageHelper } from "zotero-plugin-toolkit";
export { handlers };
const db = new Dexie("BN_Two_Way_Relation") as Dexie & {
link: Dexie.Table<LinkModel>;
@ -10,12 +13,31 @@ db.version(2).stores({
annotation: "++id, fromLibID, fromKey, toLibID, toKey, url",
});
console.log("Using Dexie v" + Dexie.semVer, db);
log("Using Dexie v" + Dexie.semVer, db);
postMessage({
type: "ready",
const funcs = {
addLink,
bulkAddLink,
rebuildLinkForNote,
getOutboundLinks,
getInboundLinks,
linkAnnotationToTarget,
getLinkTargetByAnnotation,
getAnnotationByLinkTarget,
};
const handlers = MessageHelper.wrapHandlers(funcs);
const messageServer = new MessageHelper({
canBeDestroyed: true,
dev: true,
name: "parsingWorker",
target: self,
handlers,
});
messageServer.start();
async function addLink(model: LinkModel) {
await db.link.add(model);
}
@ -29,12 +51,12 @@ async function rebuildLinkForNote(
fromKey: string,
links: LinkModel[],
) {
console.log("rebuildLinkForNote", fromLibID, fromKey, links);
log("rebuildLinkForNote", fromLibID, fromKey, links);
const collection = db.link.where({ fromLibID, fromKey });
const oldOutboundLinks = await collection.toArray();
await collection.delete().then((deleteCount) => {
console.log("Deleted " + deleteCount + " objects");
log("Deleted " + deleteCount + " objects");
return bulkAddLink(links);
});
return {
@ -43,17 +65,17 @@ async function rebuildLinkForNote(
}
async function getOutboundLinks(fromLibID: number, fromKey: string) {
console.log("getOutboundLinks", fromLibID, fromKey);
log("getOutboundLinks", fromLibID, fromKey);
return db.link.where({ fromLibID, fromKey }).toArray();
}
async function getInboundLinks(toLibID: number, toKey: string) {
console.log("getInboundLinks", toLibID, toKey);
log("getInboundLinks", toLibID, toKey);
return db.link.where({ toLibID, toKey }).toArray();
}
async function linkAnnotationToTarget(model: AnnotationModel) {
console.log("linkAnnotationToTarget", model);
log("linkAnnotationToTarget", model);
const collection = db.annotation.where({
fromLibID: model.fromLibID,
fromKey: model.fromKey,
@ -64,15 +86,19 @@ async function linkAnnotationToTarget(model: AnnotationModel) {
}
async function getLinkTargetByAnnotation(fromLibID: number, fromKey: string) {
console.log("getLinkTargetByAnnotation", fromLibID, fromKey);
log("getLinkTargetByAnnotation", fromLibID, fromKey);
return db.annotation.get({ fromLibID, fromKey });
}
async function getAnnotationByLinkTarget(toLibID: number, toKey: string) {
console.log("getAnnotationByLinkTarget", toLibID, toKey);
log("getAnnotationByLinkTarget", toLibID, toKey);
return db.annotation.get({ toLibID, toKey });
}
function log(...args: any[]) {
if (__env__ === "development") console.log("[relationWorker]", ...args);
}
interface LinkModel {
fromLibID: number;
fromKey: string;
@ -91,73 +117,3 @@ interface AnnotationModel {
toKey: string;
url: string;
}
// Handle messages from the main thread and send responses back for await
onmessage = async (event) => {
const { type, jobID, data } = event.data;
console.log("Worker received message", type, jobID, data);
switch (type) {
case "addLink":
postMessage({
type,
jobID,
result: await addLink(data),
});
break;
case "bulkAddLink":
postMessage({
type,
jobID,
result: await bulkAddLink(data),
});
break;
case "rebuildLinkForNote":
postMessage({
type,
jobID,
result: await rebuildLinkForNote(
data.fromLibID,
data.fromKey,
data.links,
),
});
break;
case "getOutboundLinks":
postMessage({
type,
jobID,
result: await getOutboundLinks(data.fromLibID, data.fromKey),
});
break;
case "getInboundLinks":
postMessage({
type,
jobID,
result: await getInboundLinks(data.toLibID, data.toKey),
});
break;
case "linkAnnotationToTarget":
postMessage({
type,
jobID,
result: await linkAnnotationToTarget(data),
});
break;
case "getLinkTargetByAnnotation":
postMessage({
type,
jobID,
result: await getLinkTargetByAnnotation(data.fromLibID, data.fromKey),
});
break;
case "getAnnotationByLinkTarget":
postMessage({
type,
jobID,
result: await getAnnotationByLinkTarget(data.toLibID, data.toKey),
});
break;
default:
break;
}
};

View File

@ -42,7 +42,7 @@ import { patchViewItems } from "./modules/viewItems";
import { getFocusedWindow } from "./utils/window";
import { registerNoteRelation } from "./modules/workspace/relation";
import { getPref, setPref } from "./utils/prefs";
import { closeRelationWorker } from "./utils/relation";
import { closeRelationServer } from "./utils/relation";
import { registerNoteLinkSection } from "./modules/workspace/link";
import { showUserGuide } from "./modules/userGuide";
import { refreshTemplatesInNote } from "./modules/template/refresh";
@ -109,7 +109,7 @@ async function onMainWindowUnload(win: Window): Promise<void> {
}
function onShutdown(): void {
closeRelationWorker();
closeRelationServer();
closeParsingServer();
ztoolkit.unregisterAll();
// Remove addon object

View File

@ -1,73 +1,47 @@
import { MessageHelper } from "zotero-plugin-toolkit";
import { config } from "../../package.json";
import { getNoteLinkParams } from "./link";
import type { handlers } from "../extras/relationWorker";
function closeRelationServer() {
if (addon.data.relation.server) {
addon.data.relation.server.destroy();
addon.data.relation.server = undefined;
}
}
async function getRelationServer() {
if (!addon.data.relation.server) {
const worker = new Worker(
`chrome://${config.addonRef}/content/scripts/relationWorker.js`,
{ name: "relationWorker" },
);
const server = new MessageHelper<typeof handlers>({
canBeDestroyed: false,
dev: true,
name: "relationWorkerMain",
target: worker,
handlers: {},
});
server.start();
await server.exec("_ping");
addon.data.relation.server = server;
}
return addon.data.relation.server!;
}
export { getRelationServer, closeRelationServer };
export {
updateNoteLinkRelation,
getNoteLinkInboundRelation,
getNoteLinkOutboundRelation,
closeRelationWorker,
linkAnnotationToTarget,
getLinkTargetByAnnotation,
getAnnotationByLinkTarget,
};
function closeRelationWorker() {
if (addon.data.relation.worker) {
addon.data.relation.worker.terminate();
addon.data.relation.worker = undefined;
}
}
async function getRelationWorker() {
if (addon.data.relation.worker) {
return addon.data.relation.worker;
}
const deferred = Zotero.Promise.defer();
const worker = new Worker(
`chrome://${config.addonRef}/content/scripts/relationWorker.js`,
);
addon.data.relation.worker = worker;
worker.addEventListener(
"message",
(e) => {
if (e.data === "ready") {
ztoolkit.log("Relation worker is ready.");
deferred.resolve();
}
},
{ once: true },
);
await deferred.promise;
return worker;
}
async function executeRelationWorker(data: {
type: string;
data: any;
}): Promise<any> {
const worker = await getRelationWorker();
const deferred = Zotero.Promise.defer();
const jobID = Zotero.Utilities.randomString(8);
let retData;
ztoolkit.log("executeRelationWorker", data, jobID);
const callback = (e: MessageEvent) => {
if (e.data.jobID === jobID) {
retData = e.data;
worker.removeEventListener("message", callback);
deferred.resolve();
}
};
worker.addEventListener("message", callback);
worker.postMessage({ ...data, jobID });
await Promise.race([deferred.promise, Zotero.Promise.delay(5000)]);
if (!retData) {
worker.removeEventListener("message", callback);
throw new Error(`Worker timeout: ${data.type}, ${jobID}`);
}
ztoolkit.log("executeRelationWorker return", retData);
return (retData as { result: any }).result;
}
async function updateNoteLinkRelation(noteID: number) {
const note = Zotero.Items.get(noteID);
const affectedNoteIDs = new Set([noteID]);
@ -99,10 +73,10 @@ async function updateNoteLinkRelation(noteID: number) {
}
}
}
const result = await executeRelationWorker({
type: "rebuildLinkForNote",
data: { fromLibID, fromKey, links: linkToData },
});
const result = await (
await getRelationServer()
).exec("rebuildLinkForNote", [fromLibID, fromKey, linkToData]);
for (const link of result.oldOutboundLinks as LinkModel[]) {
const item = Zotero.Items.getByLibraryAndKey(link.toLibID, link.toKey);
if (!item) {
@ -120,28 +94,21 @@ async function updateNoteLinkRelation(noteID: number) {
);
}
async function getNoteLinkOutboundRelation(
noteID: number,
): Promise<LinkModel[]> {
async function getNoteLinkOutboundRelation(noteID: number) {
const note = Zotero.Items.get(noteID);
const fromLibID = note.libraryID;
const fromKey = note.key;
return executeRelationWorker({
type: "getOutboundLinks",
data: { fromLibID, fromKey },
});
return (await getRelationServer()).exec("getOutboundLinks", [
fromLibID,
fromKey,
]);
}
async function getNoteLinkInboundRelation(
noteID: number,
): Promise<LinkModel[]> {
async function getNoteLinkInboundRelation(noteID: number) {
const note = Zotero.Items.get(noteID);
const toLibID = note.libraryID;
const toKey = note.key;
return executeRelationWorker({
type: "getInboundLinks",
data: { toLibID, toKey },
});
return (await getRelationServer()).exec("getInboundLinks", [toLibID, toKey]);
}
function decodeHTMLEntities(text: string) {
@ -162,31 +129,22 @@ interface LinkModel {
url: string;
}
async function linkAnnotationToTarget(model: AnnotationModel): Promise<void> {
return executeRelationWorker({
type: "linkAnnotationToTarget",
data: model,
});
async function linkAnnotationToTarget(model: AnnotationModel) {
return (await getRelationServer()).exec("linkAnnotationToTarget", [model]);
}
async function getLinkTargetByAnnotation(
fromLibID: number,
fromKey: string,
): Promise<AnnotationModel> {
return executeRelationWorker({
type: "getLinkTargetByAnnotation",
data: { fromLibID, fromKey },
});
async function getLinkTargetByAnnotation(fromLibID: number, fromKey: string) {
return (await getRelationServer()).exec("getLinkTargetByAnnotation", [
fromLibID,
fromKey,
]);
}
async function getAnnotationByLinkTarget(
toLibID: number,
toKey: string,
): Promise<AnnotationModel> {
return executeRelationWorker({
type: "getAnnotationByLinkTarget",
data: { toLibID, toKey },
});
async function getAnnotationByLinkTarget(toLibID: number, toKey: string) {
return (await getRelationServer()).exec("getAnnotationByLinkTarget", [
toLibID,
toKey,
]);
}
interface AnnotationModel {

View File

@ -40,6 +40,9 @@ export default defineConfig({
},
{
entryPoints: ["src/extras/*.*"],
define: {
__env__: `"${process.env.NODE_ENV}"`,
},
outdir: "build/addon/chrome/content/scripts",
bundle: true,
target: ["firefox115"],