diff --git a/README.md b/README.md index 87ce6af..2969aa8 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,6 @@ java.asyncOptions = { asyncSuffix: undefined, // Don't generate node-style methods taking callbacks syncSuffix: "", // Sync methods use the base name(!!) promiseSuffix: "Promise", // Generate methods returning promises, using the suffix Promise. - promisify: require('util').promisify // Needs Node.js version 8 or greater, see comment below }; java.classpath.push("commons-lang3-3.1.jar"); java.classpath.push("commons-io.jar"); @@ -259,15 +258,12 @@ java.newInstancePromise("java.util.ArrayList") * If you want the defacto standard behavior, simply don't set java.asyncOptions. * If you do provide asyncOptions, be aware that this module will not generate method variants of a given flavor if you don't provide a string value for the corresponding suffix (`asyncSuffix`, `syncSuffix`, `promiseSuffix`). In the example above, the application is configured to omit the method variants using node-style async callback functions. -* If you provide `asyncOptions.promiseSuffix` then you must also set `asyncOptions.promisify` to a function that *promisifies* a node-style async function. I.e. the provided function must take as input a function whose last argument is a node callback function, and it must return an equivalent promise-returning function. Several Promises/A+ libraries provide such functions, but it may be necessary to provide a wrapper function. See `testHelpers.js` for an example. -* For `promisify` implementation, if you are using Node.js version 8.0.0 or newer then `promisify: require('util').promisify` will work out of the box. If you need to support and older Node.js version then an implementation needs to be provided, for example, `promisify: require("when/node").lift` -* If you provide `asyncOptions.promisify` then you must provide a *non-empty* string for `asyncOptions.promiseSuffix`. * Either (but not both) `asyncSuffix` or `syncSuffix` can be the empty string. If you want the defacto standard behavior for no suffix on async methods, you must provide an empty string for `asyncSuffix`. * We've tested promises with five Promises/A+ implementations. See `testHelpers.js` for more information. * NOTE: Due to specifics of initialization order, the methods `java.newInstancePromise`, `java.callMethodPromise`, and `java.callStaticMethodPromise` are not available until the JVM has been created. You may need to call some other java method such as `java.import()` to finalize java initialization, or even better, the function `java.ensureJvm()`. ##### Special note about the exported module functions `newInstance`, `callMethod`, and `callStaticMethod`. -These methods come in both async and sync variants. If you provide the `promisify` and `promiseSuffix` attributes in asyncOptions then you'll also get the Promises/A+ variant for these three functions. However, if you change the defacto conventions for the `syncSuffix` (i.e. 'Sync') and/or `asyncSuffix` (i.e. '') it will not affect the naming for these three functions. I.e. no matter what you specify in asyncOptions, the async variants are named `newInstance`, `callMethod`, and `callStaticMethod`, and the sync variants are named `newInstanceSync`, `callMethodSync`, and `callStaticMethodSync`. +These methods come in both async and sync variants. If you provide the `promiseSuffix` attributes in asyncOptions then you'll also get the Promises/A+ variant for these three functions. However, if you change the defacto conventions for the `syncSuffix` (i.e. 'Sync') and/or `asyncSuffix` (i.e. '') it will not affect the naming for these three functions. I.e. no matter what you specify in asyncOptions, the async variants are named `newInstance`, `callMethod`, and `callStaticMethod`, and the sync variants are named `newInstanceSync`, `callMethodSync`, and `callStaticMethodSync`. ## Varargs support @@ -375,7 +371,6 @@ java.asyncOptions = { asyncSuffix: undefined, // Don't generate node-style methods taking callbacks syncSuffix: "", // Sync methods use the base name(!!) promiseSuffix: "Promise", // Generate methods returning promises, using the suffix Promise. - promisify: require('util').promisify // Needs Node.js version 8 or greater, see comment below ifReadOnlySuffix: "_alt" }; ``` @@ -383,7 +378,6 @@ java.asyncOptions = { * `asyncSuffix` Suffix for callback-based async method call signatures. * `syncSuffix` Suffix for synchronous method call signatures. * `promiseSuffix` Suffix for promise-based async method call signatures - * `promisify` Callback-to-promise transform implementation. From Node.js version 8 one can just use Node.js implementation: `promisify: require('util').promisify`. * `ifReadOnlySuffix` See [Static Member Name Conflicts](#staticMemberNameConflicts). See [Async Options](#asyncOptionsDetails) for details. diff --git a/java.d.ts b/java.d.ts index 7990efd..9395479 100644 --- a/java.d.ts +++ b/java.d.ts @@ -16,16 +16,12 @@ declare namespace NodeJavaCore { (err?: JavaError, result?: T): void; } - interface Promisify { - (fn: Function, receiver?: any): Function; - } - interface AsyncOptions { /** * Suffix for synchronous method call signatures. */ syncSuffix: string; - + /** * Suffix for callback-based async method call signatures. */ @@ -36,12 +32,6 @@ declare namespace NodeJavaCore { */ promiseSuffix?: string | undefined; - /** - * Callback-to-promise transform implementation. From Node.js version 8 one can - * just use Node.js implementation: `promisify: require('util').promisify`. - */ - promisify?: Promisify | undefined; - /** * The JavaScript object returned by `java.import(classname)` is a JavaScript constructor * Function, implemented such that you can create instances of the Java class. For example: @@ -56,9 +46,9 @@ declare namespace NodeJavaCore { interface Java { /** * Array of paths or jars to pass to the creation of the JVM. - * + * * All items must be added to the classpath before calling any other node-java methods. - * + * * @example * java.classpath.push('commons.io.jar'); * java.classpath.push('src'); @@ -67,9 +57,9 @@ declare namespace NodeJavaCore { /** * Array of options to pass to the creation of the JVM. - * + * * All items must be added to the options before calling any other node-java methods. - * + * * @example * java.options.push('-Djava.awt.headless=true'); * java.options.push('-Xmx1024m'); diff --git a/package-lock.json b/package-lock.json index 72fd77c..77f06e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,6 @@ "@eslint/js": "^9.27.0", "@types/find-root": "^1.1.4", "@types/node": "^22.15.21", - "@types/when": "^2.4.41", "chalk": "2.4.2", "eslint": "^9.27.0", "find-root": "^1.1.0", @@ -27,8 +26,7 @@ "prettier": "^3.5.3", "typescript": "^5.8.3", "typescript-eslint": "^8.32.1", - "vitest": "^3.1.3", - "when": "3.7.8" + "vitest": "^3.1.3" }, "engines": { "node": ">=7.0.0" @@ -1175,13 +1173,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/when": { - "version": "2.4.41", - "resolved": "https://registry.npmjs.org/@types/when/-/when-2.4.41.tgz", - "integrity": "sha512-o/j5X9Bnv6mMG4ZcNJur8UaU17Rl0mLbTZvWcODVVy+Xdh8LEc7s6I0CvbEuTP786LTa0OyJby5P4hI7C+ZJNg==", - "dev": true, - "license": "MIT" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.32.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", @@ -4159,12 +4150,6 @@ } } }, - "node_modules/when": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", - "dev": true - }, "node_modules/which": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", diff --git a/package.json b/package.json index c5c633e..3515871 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "@eslint/js": "^9.27.0", "@types/find-root": "^1.1.4", "@types/node": "^22.15.21", - "@types/when": "^2.4.41", "chalk": "2.4.2", "eslint": "^9.27.0", "find-root": "^1.1.0", @@ -43,8 +42,7 @@ "prettier": "^3.5.3", "typescript": "^5.8.3", "typescript-eslint": "^8.32.1", - "vitest": "^3.1.3", - "when": "3.7.8" + "vitest": "^3.1.3" }, "scripts": { "install": "node-gyp rebuild", diff --git a/src-cpp/java.cpp b/src-cpp/java.cpp index 17743cc..e124b4d 100644 --- a/src-cpp/java.cpp +++ b/src-cpp/java.cpp @@ -190,13 +190,6 @@ void Java::configureAsync(v8::Local &asyncOptions) { v8::Local suffix = suffixValue->ToString(Nan::GetCurrentContext()).ToLocalChecked(); Nan::Utf8String utf8(suffix); m_PromiseSuffix.assign(*utf8); - v8::MaybeLocal maybePromisify = - Nan::Get(asyncOptionsObj, Nan::New("promisify").ToLocalChecked()); - v8::Local promisify; - if (maybePromisify.ToLocal(&promisify) && !promisify->IsFunction()) { - fprintf(stderr, "asyncOptions.promisify must be a function"); - assert(promisify->IsFunction()); - } doPromise = true; } diff --git a/src-cpp/javaObject.cpp b/src-cpp/javaObject.cpp index d938426..8c8d33b 100644 --- a/src-cpp/javaObject.cpp +++ b/src-cpp/javaObject.cpp @@ -28,13 +28,9 @@ v8::Local promisify; if (java->DoPromise()) { - v8::Local asyncOptions = - java->handle() - ->Get(Nan::GetCurrentContext(), Nan::New("asyncOptions").ToLocalChecked()) - .ToLocalChecked() - .As(); v8::Local promisifyValue = - asyncOptions->Get(Nan::GetCurrentContext(), Nan::New("promisify").ToLocalChecked()) + java->handle() + ->Get(Nan::GetCurrentContext(), Nan::New("promisify").ToLocalChecked()) .ToLocalChecked(); promisify = promisifyValue.As(); } @@ -78,7 +74,7 @@ v8::Local argv[] = {methodCallTemplate->GetFunction(Nan::GetCurrentContext()).ToLocalChecked()}; v8::Local result = Nan::Call(promisify, recv, 1, argv).FromMaybe(v8::Local()); if (!result->IsFunction()) { - fprintf(stderr, "Promisified result is not a function -- asyncOptions.promisify must return a function.\n"); + fprintf(stderr, "Promisified result is not a function.\n"); assert(result->IsFunction()); } v8::Local promFunction = result.As(); diff --git a/src-node/nodeJavaBridge.js b/src-node/nodeJavaBridge.js index ecfbbef..01f3997 100644 --- a/src-node/nodeJavaBridge.js +++ b/src-node/nodeJavaBridge.js @@ -4,6 +4,8 @@ process.env.PATH += require("../build/jvm_dll_path.json"); const path = require("path"); const fs = require("fs"); +const util = require("util"); + let binaryPath = null; try { if (fs.statSync && fs.statSync(path.join(__dirname, "../build/Debug/nodejavabridge_bindings.node")).isFile()) { @@ -19,6 +21,7 @@ if (!binaryPath) { const bindings = require(binaryPath); const java = (module.exports = new bindings.Java()); +java.promisify = util.promisify; java.classpath.push(path.resolve(__dirname, "../src-java/commons-lang3-node-java.jar")); java.classpath.push(path.resolve(__dirname, __dirname, "../src-java")); java.classpath.pushDir = function (dir) { @@ -146,18 +149,15 @@ async function initializeAll() { // This function ensures that the JVM has been launched, asynchronously. The application can be notified // when the JVM is fully created via either a node callback function, or via a promise. // If the parameter `callback` is provided, it is assume be a node callback function. -// If the parameter is not provided, and java.asyncOptions.promisify has been specified, -// then this function will return a promise, by promisifying itself and then calling that -// promisified function. // This function may be called multiple times -- the 2nd and subsequent calls are no-ops. // However, once this method has been called (or the JVM is launched as a side effect of calling other java // methods), then clients can no longer use the registerClient API. java.ensureJvm = function (callback) { // First see if the promise-style API should be used. // This must be done first in order to ensure the proper API is used. - if (typeof callback === "undefined" && java.asyncOptions && typeof java.asyncOptions.promisify === "function") { + if (typeof callback === "undefined") { // Create a promisified version of this function. - const launchJvmPromise = java.asyncOptions.promisify(java.ensureJvm.bind(java)); + const launchJvmPromise = util.promisify(java.ensureJvm.bind(java)); // Call the promisified function, returning its result, which should be a promise. return launchJvmPromise(); } @@ -194,16 +194,11 @@ java.onJvmCreated = function () { throw new Error("In asyncOptions, syncSuffix must be defined and must a string"); } const promiseSuffix = java.asyncOptions.promiseSuffix; - const promisify = java.asyncOptions.promisify; - if (typeof promiseSuffix === "string" && typeof promisify === "function") { + if (typeof promiseSuffix === "string") { const methods = ["newInstance", "callMethod", "callStaticMethod"]; methods.forEach(function (name) { - java[name + promiseSuffix] = promisify(java[name]); + java[name + promiseSuffix] = util.promisify(java[name]); }); - } else if (typeof promiseSuffix === "undefined" && typeof promisify === "undefined") { - // no promises - } else { - throw new Error("In asyncOptions, if either promiseSuffix or promisify is defined, both most be."); } if (typeof java.asyncOptions.ifReadOnlySuffix === "string" && java.asyncOptions.ifReadOnlySuffix !== "") { @@ -273,10 +268,8 @@ java.import = function (name) { } } - let promisify = undefined; let promiseSuffix; - if (java.asyncOptions && java.asyncOptions.promisify) { - promisify = java.asyncOptions.promisify; + if (java.asyncOptions) { promiseSuffix = java.asyncOptions.promiseSuffix; } @@ -297,9 +290,9 @@ java.import = function (name) { result[asyncName] = callStaticMethod.bind(java, name, methodName); } - if (promisify && typeof promiseSuffix === "string") { + if (typeof promiseSuffix === "string") { const promiseName = usableName(methodName + promiseSuffix); - result[promiseName] = promisify(callStaticMethod.bind(java, name, methodName)); + result[promiseName] = util.promisify(callStaticMethod.bind(java, name, methodName)); } } } diff --git a/testAsyncOptions/allThreeSuffix.test.ts b/testAsyncOptions/allThreeSuffix.test.ts index 5765076..72ebe81 100644 --- a/testAsyncOptions/allThreeSuffix.test.ts +++ b/testAsyncOptions/allThreeSuffix.test.ts @@ -12,8 +12,6 @@ describe("allThreeSuffix", () => { syncSuffix: "Sync", asyncSuffix: "Async", promiseSuffix: "Promise", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }); }); diff --git a/testAsyncOptions/clientPBeforeError.test.ts b/testAsyncOptions/clientPBeforeError.test.ts index cd6077f..0d8e498 100644 --- a/testAsyncOptions/clientPBeforeError.test.ts +++ b/testAsyncOptions/clientPBeforeError.test.ts @@ -7,8 +7,6 @@ describe("clientPBeforeError", () => { { syncSuffix: "Sync", promiseSuffix: "Promise", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }, { beforeInit: async (java) => { diff --git a/testAsyncOptions/clientPBeforeThrows.test.ts b/testAsyncOptions/clientPBeforeThrows.test.ts index 901a65d..cb7d725 100644 --- a/testAsyncOptions/clientPBeforeThrows.test.ts +++ b/testAsyncOptions/clientPBeforeThrows.test.ts @@ -7,8 +7,6 @@ describe("clientPBeforeThrows", () => { { syncSuffix: "Sync", promiseSuffix: "Promise", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }, { beforeInit: async (java) => { diff --git a/testAsyncOptions/defactoPlusPromise.test.ts b/testAsyncOptions/defactoPlusPromise.test.ts index 3149f5a..d41a4b2 100644 --- a/testAsyncOptions/defactoPlusPromise.test.ts +++ b/testAsyncOptions/defactoPlusPromise.test.ts @@ -13,8 +13,6 @@ describe("defactoPlusPromise", () => { syncSuffix: "Sync", asyncSuffix: "", promiseSuffix: "Promise", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }, { beforeInit: (java) => { diff --git a/testAsyncOptions/ensureJvmPromise.test.ts b/testAsyncOptions/ensureJvmPromise.test.ts new file mode 100644 index 0000000..6e56a71 --- /dev/null +++ b/testAsyncOptions/ensureJvmPromise.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, test } from "vitest"; +import { getJava } from "../testHelpers"; + +describe("ensureJvmPromise", () => { + test("calling ensureJvm as a promise", async () => { + await getJava( + { + syncSuffix: "Sync", + asyncSuffix: "", + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + await java.ensureJvm(); + expect(java.isJvmCreated()).toBeTruthy(); + }, + } + ); + }); +}); diff --git a/testAsyncOptions/invalidLaunch.test.ts b/testAsyncOptions/invalidLaunch.test.ts index 7de9f02..53fefb7 100644 --- a/testAsyncOptions/invalidLaunch.test.ts +++ b/testAsyncOptions/invalidLaunch.test.ts @@ -2,34 +2,11 @@ import { describe, expect, test } from "vitest"; import { getJava } from "../testHelpers"; describe("invalidLaunch", () => { - test("calling ensureJvm as a promise when no promisify function supplied", async () => { - await getJava( - { - syncSuffix: "Sync", - asyncSuffix: "", - }, - { - beforeInit: (java) => { - expect(java.isJvmCreated()).toBeFalsy(); - - // First show that if asyncOptions.promisify is undefined, using the promise variant of ensureJvm throws an error. - expect(() => { - void java.ensureJvm(); - }).toThrow(/requires its one argument to be a callback function/); - - expect(java.isJvmCreated()).toBeFalsy(); - }, - } - ); - }); - test("callbackNotAFunction", async () => { await getJava( { syncSuffix: "", promiseSuffix: "P", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }, { beforeInit: (java) => { @@ -51,8 +28,6 @@ describe("invalidLaunch", () => { { syncSuffix: "", promiseSuffix: "P", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }, { beforeInit: async (java) => { diff --git a/testAsyncOptions/noAsync.test.ts b/testAsyncOptions/noAsync.test.ts index c4d434f..d3b16e2 100644 --- a/testAsyncOptions/noAsync.test.ts +++ b/testAsyncOptions/noAsync.test.ts @@ -12,8 +12,6 @@ describe("noAsync", () => { { syncSuffix: "Sync", promiseSuffix: "Promise", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, }, { beforeInit: (java) => { diff --git a/testAsyncOptions/syncDefaultPlusPromise.test.ts b/testAsyncOptions/syncDefaultPlusPromise.test.ts index 5a4d222..21e214f 100644 --- a/testAsyncOptions/syncDefaultPlusPromise.test.ts +++ b/testAsyncOptions/syncDefaultPlusPromise.test.ts @@ -12,8 +12,6 @@ describe("syncDefaultPlusPromise", () => { java = await getJava({ syncSuffix: "", promiseSuffix: "P", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }); }); diff --git a/testHelpers.ts b/testHelpers.ts index 8af6b8a..7e44ee6 100644 --- a/testHelpers.ts +++ b/testHelpers.ts @@ -30,8 +30,6 @@ async function _getJava(asyncOptions?: AsyncOptions | null, options?: GetJavaOpt syncSuffix: "Sync", asyncSuffix: "", promiseSuffix: "Promise", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when }; }