commit 6d9021a18dc673d63206f6531da31e147e571550
parent c80cbe224219a230240dafbaeec0ed83563b3b56
Author: Florian Dold <dold@taler.net>
Date: Thu, 20 Aug 2026 19:06:44 +0200
wallet-core: run Node crypto workers under ESM
Diffstat:
3 files changed, 51 insertions(+), 65 deletions(-)
diff --git a/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.test.ts b/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.test.ts
@@ -0,0 +1,32 @@
+/*
+ This file is part of GNU Taler
+ (C) 2026 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import assert from "node:assert";
+import { test } from "node:test";
+import { CryptoDispatcher } from "./crypto-dispatcher.js";
+import { NodeThreadCryptoWorkerFactory } from "./nodeThreadWorker.js";
+
+test("node worker threads execute crypto RPCs in an ESM package", async () => {
+ const dispatcher = new CryptoDispatcher(new NodeThreadCryptoWorkerFactory());
+ try {
+ const result = await dispatcher.doRpc("hashString", 0, {
+ str: "worker-thread-test",
+ });
+ assert.strictEqual(typeof (result as { h?: unknown }).h, "string");
+ } finally {
+ dispatcher.stop();
+ }
+});
diff --git a/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts b/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts
@@ -19,7 +19,7 @@
*/
import { Logger } from "@gnu-taler/taler-util";
import os from "node:os";
-import url from "node:url";
+import { parentPort, Worker } from "node:worker_threads";
import { nativeCryptoR } from "../cryptoImplementation.js";
import { CryptoWorkerFactory } from "./crypto-dispatcher.js";
import { CryptoWorker } from "./cryptoWorkerInterface.js";
@@ -27,45 +27,21 @@ import { processRequestWithImpl } from "./worker-common.js";
const logger = new Logger("nodeThreadWorker.ts");
-const f = import.meta.url
- ? url.fileURLToPath(import.meta.url)
- : "__not_available__";
-
const workerCode = `
- // Try loading the glue library for embedded
- try {
- require("akono");
- } catch (e) {
- try {
- require("iono");
- } catch (e2) {
- // Probably we're not on embedded ...
+ const { parentPort } = require("node:worker_threads");
+ (async () => {
+ const tw = await import(${JSON.stringify(import.meta.url)});
+ if (typeof tw.handleWorkerMessage !== "function") {
+ throw Error("module loaded for crypto worker lacks handleWorkerMessage");
}
- }
- const worker_threads = require('worker_threads');
- const parentPort = worker_threads.parentPort;
- let tw;
- try {
- tw = require("${f}");
- } catch (e) {
- console.warn("could not load from ${f}");
- }
- if (!tw) {
- try {
- tw = require("@gnu-taler/taler-wallet-embedded");
- } catch (e) {
- console.warn("could not load taler-wallet-embedded either");
- throw e;
+ if (typeof tw.handleWorkerError !== "function") {
+ throw Error("module loaded for crypto worker lacks handleWorkerError");
}
- }
- if (typeof tw.handleWorkerMessage !== "function") {
- throw Error("module loaded for crypto worker lacks handleWorkerMessage");
- }
- if (typeof tw.handleWorkerError !== "function") {
- throw Error("module loaded for crypto worker lacks handleWorkerError");
- }
- parentPort.on("message", tw.handleWorkerMessage);
- parentPort.on("error", tw.handleWorkerError);
+ parentPort.on("message", tw.handleWorkerMessage);
+ parentPort.on("error", tw.handleWorkerError);
+ })().catch((e) => {
+ setImmediate(() => { throw e; });
+ });
`;
/**
@@ -76,13 +52,8 @@ export function handleWorkerMessage(msg: any): void {
const handleRequest = async (): Promise<void> => {
const responseMsg = await processRequestWithImpl(msg, nativeCryptoR);
try {
- const _r = "require";
- const worker_threads: typeof import("worker_threads") =
- module[_r]("worker_threads");
- // const worker_threads = require("worker_threads");
- const p = worker_threads.parentPort;
- if (p) {
- p.postMessage(responseMsg);
+ if (parentPort) {
+ parentPort.postMessage(responseMsg);
} else {
logger.error("parent port not available (not running in thread?");
}
@@ -101,9 +72,6 @@ export function handleWorkerError(e: Error): void {
export class NodeThreadCryptoWorkerFactory implements CryptoWorkerFactory {
startWorker(): CryptoWorker {
- if (typeof require === "undefined") {
- throw Error("cannot make worker, require(...) not defined");
- }
return new NodeThreadCryptoWorker();
}
@@ -126,15 +94,12 @@ class NodeThreadCryptoWorker implements CryptoWorker {
*/
onerror: undefined | ((m: any) => void);
- private nodeWorker: import("worker_threads").Worker;
+ private nodeWorker: Worker;
constructor() {
- const _r = "require";
- const worker_threads = module[_r]("worker_threads");
-
logger.trace("starting node crypto worker");
- this.nodeWorker = new worker_threads.Worker(workerCode, { eval: true });
+ this.nodeWorker = new Worker(workerCode, { eval: true });
this.nodeWorker.on("error", (err: Error) => {
logger.error("error in node worker:", err);
if (this.onerror) {
diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts
@@ -261,19 +261,8 @@ export async function createNativeWalletHost2(
logger.info("using synchronous crypto worker");
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
} else if (cryptoWorkerType === "node-worker-thread") {
- try {
- // Try if we have worker threads available, fails in older node versions.
- const _r = "require";
- const worker_threads = module[_r]("worker_threads");
- // require("worker_threads");
- workerFactory = new NodeThreadCryptoWorkerFactory();
- logger.info("using node thread crypto worker");
- } catch (e) {
- logger.warn(
- "worker threads not available, falling back to synchronous workers",
- );
- workerFactory = new SynchronousCryptoWorkerFactoryPlain();
- }
+ workerFactory = new NodeThreadCryptoWorkerFactory();
+ logger.info("using node thread crypto worker");
} else {
throw Error(`unsupported crypto worker type '${cryptoWorkerType}'`);
}