From 557e6fabe87bcf7a410212447c2d29111bdc7819 Mon Sep 17 00:00:00 2001 From: Jiayu Liu Date: Mon, 25 May 2026 08:28:14 +0800 Subject: [PATCH 1/3] Switch JS/Node generator and runtime from Q to native Promise Removes the runtime dependency on the `q` Promise library and switches the JS/TS code generator to emit native Promise by default. - Compiler (t_js_generator.cc): in non-ES6 mode the processor emits `new Promise((resolve) => resolve(handler.fn(args)))` instead of `Q.fcall(...)`, and the Node client emits `new Promise((resolve, reject) => ...)` instead of `Q.defer()` / `_defer.promise`. The non-ES6 JS/TS imports no longer pull in `Q` by default. - New compiler flag `js:native_promise=[true|false]` (default `true`). Setting it to `false` restores the legacy `Q.fcall` / `Q.defer` output and emits `const Q = require('q');` / `import Q = require('q');` directly (no longer routed through `thrift.Q`), so legacy users only need `q` in their own `node_modules`. - Runtime: removed `exports.Q = require("q")` from lib/nodejs/lib/thrift/index.js and browser.js, dropped `q` from dependencies and `@types/q` from devDependencies, and pruned the matching package-lock.json entries. - Tests: lib/nodets/test/test_driver.ts and test_handler.ts now use native `Promise` / `.catch()` instead of `Q.IPromise` / `Q.resolve` / `.fail()`. Renamed the test case "Q Promise Client Tests" to "Promise Client Tests". `AsyncThriftTestHandler` return types changed to `Promise` (the callback delivers the value and the body returns `Promise.resolve()`, which native Promise types strictly). Client: js,nodejs Co-Authored-By: Claude Opus 4.7 (1M context) --- .../cpp/src/thrift/generate/t_js_generator.cc | 103 +++++++++---- lib/nodejs/lib/thrift/browser.js | 1 - lib/nodejs/lib/thrift/index.js | 1 - lib/nodejs/test/package-lock.json | 2 - lib/nodets/test/test_driver.ts | 33 ++--- lib/nodets/test/test_handler.ts | 139 +++++++++--------- package-lock.json | 17 --- package.json | 2 - 8 files changed, 160 insertions(+), 138 deletions(-) diff --git a/compiler/cpp/src/thrift/generate/t_js_generator.cc b/compiler/cpp/src/thrift/generate/t_js_generator.cc index a7ee220282f..944ecd10449 100644 --- a/compiler/cpp/src/thrift/generate/t_js_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_js_generator.cc @@ -70,6 +70,7 @@ class t_js_generator : public t_oop_generator { gen_es6_ = false; gen_esm_ = false; gen_episode_file_ = false; + gen_native_promise_ = true; bool with_ns_ = false; @@ -90,6 +91,12 @@ class t_js_generator : public t_oop_generator { parse_imports(program, iter->second); } else if (iter->first.compare("thrift_package_output_directory") == 0) { parse_thrift_package_output_directory(iter->second); + } else if (iter->first.compare("native_promise") == 0) { + if (iter->second == "false" || iter->second == "0" || iter->second == "no") { + gen_native_promise_ = false; + } else { + gen_native_promise_ = true; + } } else { throw std::invalid_argument("unknown option js:" + iter->first); } @@ -388,6 +395,12 @@ class t_js_generator : public t_oop_generator { */ bool gen_episode_file_; + /** + * True (default) if generated code should use native Promise; false to emit + * the legacy Q-based output (Q.fcall / Q.defer and imports from the 'q' package). + */ + bool gen_native_promise_; + /** * The name of the defined module(s), for TypeScript Definition Files. */ @@ -530,11 +543,11 @@ string t_js_generator::js_includes() { result += js_const_type_ + "thrift = require('thrift');\n" + js_const_type_ + "Thrift = thrift.Thrift;\n"; } - if (!gen_es6_) { + if (!gen_native_promise_ && !gen_es6_) { if (gen_esm_) { - result += "import { Q } from 'thrift';\n"; + result += "import Q from 'q';\n"; } else { - result += js_const_type_ + "Q = thrift.Q;\n"; + result += js_const_type_ + "Q = require('q');\n"; } } if (gen_esm_) { @@ -556,13 +569,17 @@ string t_js_generator::js_includes() { */ string t_js_generator::ts_includes() { if (gen_node_) { - return string( + string result = "import thrift = require('thrift');\n" - "import Thrift = thrift.Thrift;\n" - "import Q = thrift.Q;\n" + "import Thrift = thrift.Thrift;\n"; + if (!gen_native_promise_) { + result += "import Q = require('q');\n"; + } + result += "import Int64 = require('node-int64');\n" "import { v4 as uuid } from 'uuid';\n" - "type uuid = string;"); + "type uuid = string;"; + return result; } return string( "import Int64 = require('node-int64');\n" @@ -575,11 +592,14 @@ string t_js_generator::ts_includes() { */ string t_js_generator::ts_service_includes() { if (gen_node_) { - return string( + string result = "import thrift = require('thrift');\n" - "import Thrift = thrift.Thrift;\n" - "import Q = thrift.Q;\n" - "import Int64 = require('node-int64');"); + "import Thrift = thrift.Thrift;\n"; + if (!gen_native_promise_) { + result += "import Q = require('q');\n"; + } + result += "import Int64 = require('node-int64');"; + return result; } return string("import Int64 = require('node-int64');"); } @@ -1570,7 +1590,7 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* << ".length === " << fields.size() << ") {" << '\n'; indent_up(); - if (gen_es6_) { + if (gen_es6_ || gen_native_promise_) { indent(f_service_) << "new Promise((resolve) => resolve(this._handler." << tfunction->get_name() << ".bind(this._handler)(" << '\n'; } else { string maybeComma = (fields.size() > 0 ? "," : ""); @@ -1587,6 +1607,8 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* if (gen_es6_) { indent(f_service_) << "))).then(result => {" << '\n'; + } else if (gen_native_promise_) { + indent(f_service_) << "))).then(function(result) {" << '\n'; } else { indent(f_service_) << ").then(function(result) {" << '\n'; } @@ -1951,22 +1973,44 @@ void t_js_generator::generate_service_client(t_service* tservice) { f_service_ << indent() << "this._seqid = this.new_seqid();" << '\n' << indent() << "if (callback === undefined) {" << '\n'; indent_up(); - f_service_ << indent() << js_const_type_ << "_defer = Q.defer();" << '\n' << indent() - << "this._reqs[this.seqid()] = function(error, result) {" << '\n'; - indent_up(); - indent(f_service_) << "if (error) {" << '\n'; - indent_up(); - indent(f_service_) << "_defer.reject(error);" << '\n'; - indent_down(); - indent(f_service_) << "} else {" << '\n'; - indent_up(); - indent(f_service_) << "_defer.resolve(result);" << '\n'; - indent_down(); - indent(f_service_) << "}" << '\n'; - indent_down(); - indent(f_service_) << "};" << '\n'; - f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << '\n' - << indent() << "return _defer.promise;" << '\n'; + if (gen_native_promise_) { + f_service_ << indent() << js_const_type_ << "self = this;" << '\n' << indent() + << "return new Promise(function(resolve, reject) {" << '\n'; + indent_up(); + f_service_ << indent() << "self._reqs[self.seqid()] = function(error, result) {" << '\n'; + indent_up(); + indent(f_service_) << "if (error) {" << '\n'; + indent_up(); + indent(f_service_) << "reject(error);" << '\n'; + indent_down(); + indent(f_service_) << "} else {" << '\n'; + indent_up(); + indent(f_service_) << "resolve(result);" << '\n'; + indent_down(); + indent(f_service_) << "}" << '\n'; + indent_down(); + indent(f_service_) << "};" << '\n'; + f_service_ << indent() << "self.send_" << funname << "(" << arglist << ");" << '\n'; + indent_down(); + indent(f_service_) << "});" << '\n'; + } else { + f_service_ << indent() << js_const_type_ << "_defer = Q.defer();" << '\n' << indent() + << "this._reqs[this.seqid()] = function(error, result) {" << '\n'; + indent_up(); + indent(f_service_) << "if (error) {" << '\n'; + indent_up(); + indent(f_service_) << "_defer.reject(error);" << '\n'; + indent_down(); + indent(f_service_) << "} else {" << '\n'; + indent_up(); + indent(f_service_) << "_defer.resolve(result);" << '\n'; + indent_down(); + indent(f_service_) << "}" << '\n'; + indent_down(); + indent(f_service_) << "};" << '\n'; + f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << '\n' + << indent() << "return _defer.promise;" << '\n'; + } indent_down(); indent(f_service_) << "} else {" << '\n'; indent_up(); @@ -3132,6 +3176,9 @@ THRIFT_REGISTER_GENERATOR(js, " ts: Generate TypeScript definition files.\n" " with_ns: Create global namespace objects when using node.js\n" " es6: Create ES6 code with Promises\n" + " native_promise=[true|false]:\n" + " Use native Promise (default true). Set to false to\n" + " emit legacy Q-based output (requires the 'q' package).\n" " thrift_package_output_directory=:\n" " Generate episode file and use the as prefix\n" " imports=:\n" diff --git a/lib/nodejs/lib/thrift/browser.js b/lib/nodejs/lib/thrift/browser.js index 5c843d07137..b0d409d94f2 100644 --- a/lib/nodejs/lib/thrift/browser.js +++ b/lib/nodejs/lib/thrift/browser.js @@ -36,7 +36,6 @@ exports.createOhosClient = ohosConnection.createOhosClient; exports.createClient = require("./create_client"); exports.Int64 = require("node-int64"); -exports.Q = require("q"); var mpxProtocol = require("./multiplexed_protocol"); exports.Multiplexer = mpxProtocol.Multiplexer; diff --git a/lib/nodejs/lib/thrift/index.js b/lib/nodejs/lib/thrift/index.js index 5f0010f1ed0..f62e6005a26 100644 --- a/lib/nodejs/lib/thrift/index.js +++ b/lib/nodejs/lib/thrift/index.js @@ -56,7 +56,6 @@ var web_server = require("./web_server"); exports.createWebServer = web_server.createWebServer; exports.Int64 = require("node-int64"); -exports.Q = require("q"); var mpxProcessor = require("./multiplexed_processor"); var mpxProtocol = require("./multiplexed_protocol"); diff --git a/lib/nodejs/test/package-lock.json b/lib/nodejs/test/package-lock.json index bcd8e4437f9..a41d14df69c 100644 --- a/lib/nodejs/test/package-lock.json +++ b/lib/nodejs/test/package-lock.json @@ -17,7 +17,6 @@ "browser-or-node": "^1.2.1", "isomorphic-ws": "^4.0.1", "node-int64": "^0.4.0", - "q": "^1.5.0", "uuid": "^13.0.0", "ws": "^5.2.3" }, @@ -25,7 +24,6 @@ "@eslint/js": "^9.18.0", "@types/node": "^22.10.5", "@types/node-int64": "^0.4.29", - "@types/q": "^1.5.1", "buffer-equals": "^1.0.4", "commander": "^13.0.0", "connect": "^3.6.6", diff --git a/lib/nodets/test/test_driver.ts b/lib/nodets/test/test_driver.ts index 604d5e5836d..c5e54a3f7b1 100644 --- a/lib/nodets/test/test_driver.ts +++ b/lib/nodets/test/test_driver.ts @@ -30,7 +30,6 @@ import test = require("tape"); import ttypes = require("./gen-nodejs/ThriftTest_types"); import ThriftTest = require("./gen-nodejs/ThriftTest"); import thrift = require("thrift"); -import Q = thrift.Q; import TException = thrift.Thrift.TException; var Int64 = require("node-int64"); import testCases = require("./test-cases"); @@ -129,7 +128,7 @@ export function ThriftTestDriverPromise( client: ThriftTest.Client, callback: (status: string) => void, ) { - test("Q Promise Client Tests", function (assert) { + test("Promise Client Tests", function (assert) { var checkRecursively = makeRecursiveCheck(assert); function fail(msg: string) { @@ -149,7 +148,7 @@ export function ThriftTestDriverPromise( .then(function (actual: any) { assertionFn(actual, expected, fnName); }) - .fail(fail("fnName")); + .catch(fail("fnName")); }; } @@ -161,58 +160,58 @@ export function ThriftTestDriverPromise( ); testCases.deep.forEach(makeAsserter(assert.deepEqual)); - Q.resolve(client.testStruct(testCases.out)) + Promise.resolve(client.testStruct(testCases.out)) .then(function (response) { checkRecursively(testCases.out, response, "testStruct"); }) - .fail(fail("testStruct")); + .catch(fail("testStruct")); - Q.resolve(client.testNest(testCases.out2)) + Promise.resolve(client.testNest(testCases.out2)) .then(function (response) { checkRecursively(testCases.out2, response, "testNest"); }) - .fail(fail("testNest")); + .catch(fail("testNest")); - Q.resolve(client.testInsanity(testCases.crazy)) + Promise.resolve(client.testInsanity(testCases.crazy)) .then(function (response) { checkRecursively(testCases.insanity, response, "testInsanity"); }) - .fail(fail("testInsanity")); + .catch(fail("testInsanity")); - Q.resolve(client.testException("TException")) + Promise.resolve(client.testException("TException")) .then(function (response) { fail("testException: TException"); }) - .fail(function (err) { + .catch(function (err) { assert.ok(err instanceof TException); }); - Q.resolve(client.testException("Xception")) + Promise.resolve(client.testException("Xception")) .then(function (response) { fail("testException: Xception"); }) - .fail(function (err) { + .catch(function (err) { assert.ok(err instanceof ttypes.Xception); assert.equal(err.errorCode, 1001); assert.equal("Xception", err.message); }); - Q.resolve(client.testException("no Exception")) + Promise.resolve(client.testException("no Exception")) .then(function (response) { assert.equal(undefined, response); //void }) - .fail(fail("testException")); + .catch(fail("testException")); client.testOneway(0, fail("testOneway: should not answer")); checkOffByOne(function (done) { - Q.resolve(client.testI32(-1)) + Promise.resolve(client.testI32(-1)) .then(function (response) { assert.equal(-1, response); assert.end(); done(); }) - .fail(fail("checkOffByOne")); + .catch(fail("checkOffByOne")); }, callback); }); } diff --git a/lib/nodets/test/test_handler.ts b/lib/nodets/test/test_handler.ts index 9e74a6b11d5..f4d72f43813 100644 --- a/lib/nodets/test/test_handler.ts +++ b/lib/nodets/test/test_handler.ts @@ -23,15 +23,14 @@ import ttypes = require("./gen-nodejs/ThriftTest_types"); import thrift = require("thrift"); import Thrift = thrift.Thrift; -import Q = require("q"); import Int64 = require("node-int64"); import { v4 as uuid } from "uuid"; type uuid = string; export class SyncThriftTestHandler { - testVoid(): Q.IPromise { + testVoid(): Promise { //console.log('testVoid()'); - return Q.resolve(undefined); + return Promise.resolve(); } testMapMap(hello: number) { //console.log('testMapMap(' + hello + ')'); @@ -46,9 +45,9 @@ export class SyncThriftTestHandler { mapmap[4] = pos; mapmap[-4] = neg; - return Q.resolve(mapmap); + return Promise.resolve(mapmap); } - testInsanity(argument: ttypes.Insanity): Q.IPromise<{ [k: number]: any }> { + testInsanity(argument: ttypes.Insanity): Promise<{ [k: number]: any }> { const first_map: { [k: number]: any } = []; const second_map: { [k: number]: any } = []; @@ -62,7 +61,7 @@ export class SyncThriftTestHandler { insane[1] = first_map; insane[2] = second_map; - return Q.resolve(insane); + return Promise.resolve(insane); } testMulti( arg0: any, @@ -77,9 +76,9 @@ export class SyncThriftTestHandler { hello.byte_thing = arg0; hello.i32_thing = arg1; hello.i64_thing = arg2; - return Q.resolve(hello); + return Promise.resolve(hello); } - testException(arg: string): Q.IPromise { + testException(arg: string): Promise { if (arg === "Xception") { var x = new ttypes.Xception(); x.errorCode = 1001; @@ -88,7 +87,7 @@ export class SyncThriftTestHandler { } else if (arg === "TException") { throw new Thrift.TException(arg); } else { - return Q.resolve(); + return Promise.resolve(); } } testMultiException(arg0: string, arg1: string) { @@ -107,57 +106,57 @@ export class SyncThriftTestHandler { var res = new ttypes.Xtruct(); res.string_thing = arg1; - return Q.resolve(res); + return Promise.resolve(res); } testOneway(sleepFor: number) {} testString(thing: string) { - return Q.resolve(thing); + return Promise.resolve(thing); } testBool(thing: boolean) { - return Q.resolve(thing); + return Promise.resolve(thing); } testByte(thing: number) { - return Q.resolve(thing); + return Promise.resolve(thing); } testI32(thing: number) { - return Q.resolve(thing); + return Promise.resolve(thing); } testI64(thing: number) { - return Q.resolve(thing); + return Promise.resolve(thing); } testDouble(thing: number) { - return Q.resolve(thing); + return Promise.resolve(thing); } testBinary(thing: Buffer) { - return Q.resolve(thing); + return Promise.resolve(thing); } testUuid(thing: uuid) { - return Q.resolve(thing); + return Promise.resolve(thing); } testStruct(thing: ttypes.Xtruct) { - return Q.resolve(thing); + return Promise.resolve(thing); } testNest(thing: ttypes.Xtruct2) { - return Q.resolve(thing); + return Promise.resolve(thing); } testMap(thing: { [k: number]: number }) { - return Q.resolve(thing); + return Promise.resolve(thing); } testStringMap(thing: { [k: string]: string }) { - return Q.resolve(thing); + return Promise.resolve(thing); } testSet(thing: number[]) { - return Q.resolve(thing); + return Promise.resolve(thing); } testList(thing: number[]) { - return Q.resolve(thing); + return Promise.resolve(thing); } testEnum(thing: ttypes.Numberz) { - return Q.resolve(thing); + return Promise.resolve(thing); } testTypedef(thing: number) { - return Q.resolve(thing); + return Promise.resolve(thing); } } @@ -167,9 +166,9 @@ export class AsyncThriftTestHandler { this.syncHandler = new SyncThriftTestHandler(); } - testVoid(callback: (result: void) => void): Q.IPromise { + testVoid(callback: (result: void) => void): Promise { callback(undefined); - return Q.resolve(); + return Promise.resolve(); } testMapMap( hello: number, @@ -177,7 +176,7 @@ export class AsyncThriftTestHandler { err: any, result: { [k: number]: { [k: number]: number } }, ) => void, - ): Q.IPromise<{ [k: number]: { [k: number]: number } }> { + ): Promise { var mapmap: { [key: number]: { [key: number]: number } } = []; var pos: { [key: number]: number } = []; var neg: { [key: number]: number } = []; @@ -189,12 +188,12 @@ export class AsyncThriftTestHandler { mapmap[-4] = neg; callback(null, mapmap); - return Q.resolve(); + return Promise.resolve(); } testInsanity( argument: ttypes.Insanity, callback?: (err: any, result: { [k: number]: any }) => void, - ): Q.IPromise<{ [k: number]: any }> { + ): Promise { const first_map: { [k: number]: any } = []; const second_map: { [k: number]: any } = []; @@ -211,7 +210,7 @@ export class AsyncThriftTestHandler { if (callback !== undefined) { callback(null, insane); } - return Q.resolve(); + return Promise.resolve(); } testMulti( arg0: any, @@ -221,12 +220,12 @@ export class AsyncThriftTestHandler { arg4: ttypes.Numberz, arg5: number, result: Function, - ): Q.IPromise { + ): Promise { var hello = this.syncHandler.testMulti(arg0, arg1, arg2, arg3, arg4, arg5); hello.then((hello) => result(null, hello)); - return Q.resolve(); + return Promise.resolve(); } - testException(arg: string, result: (err: any) => void): Q.IPromise { + testException(arg: string, result: (err: any) => void): Promise { if (arg === "Xception") { var x = new ttypes.Xception(); x.errorCode = 1001; @@ -237,13 +236,13 @@ export class AsyncThriftTestHandler { } else { result(null); } - return Q.resolve(); + return Promise.resolve(); } testMultiException( arg0: string, arg1: string, result: (err: any, res?: ttypes.Xtruct) => void, - ): Q.IPromise { + ): Promise { if (arg0 === "Xception") { var x = new ttypes.Xception(); x.errorCode = 1001; @@ -260,7 +259,7 @@ export class AsyncThriftTestHandler { res.string_thing = arg1; result(null, res); } - return Q.resolve(); + return Promise.resolve(); } testOneway(sleepFor: number, result: Function) { this.syncHandler.testOneway(sleepFor); @@ -268,113 +267,113 @@ export class AsyncThriftTestHandler { testString( thing: string, callback: (err: any, result: string) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testByte( thing: number, callback: (err: any, result: number) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testBool( thing: boolean, callback: (err: any, result: boolean) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testI32( thing: number, callback: (err: any, result: number) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testI64( thing: number, callback: (err: any, result: number) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testDouble( thing: number, callback: (err: any, result: number) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testBinary( thing: Buffer, callback: (err: any, result: Buffer) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testUuid( thing: uuid, callback: (err: any, result: uuid) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testStruct( thing: ttypes.Xtruct, callback: (err: any, result: ttypes.Xtruct) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testNest( thing: ttypes.Xtruct2, callback: (err: any, result: ttypes.Xtruct2) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testMap( thing: { [k: number]: number }, callback: (err: any, result: { [k: number]: number }) => void, - ): Q.IPromise<{ [k: number]: number }> { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testStringMap( thing: { [k: string]: string }, callback: (err: any, result: { [k: string]: string }) => void, - ): Q.IPromise<{ [k: string]: string }> { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testSet( thing: number[], callback: (err: any, result: number[]) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testList( thing: number[], callback: (err: any, result: number[]) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testEnum( thing: ttypes.Numberz, callback: (err: any, result: ttypes.Numberz) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } testTypedef( thing: number, callback: (err: any, result: number) => void, - ): Q.IPromise { + ): Promise { callback(null, thing); - return Q.resolve(); + return Promise.resolve(); } } diff --git a/package-lock.json b/package-lock.json index e8ce4f15161..0d5981afa61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "browser-or-node": "^1.2.1", "isomorphic-ws": "^4.0.1", "node-int64": "^0.4.0", - "q": "^1.5.0", "uuid": "^14.0.0", "ws": "^5.2.3" }, @@ -20,7 +19,6 @@ "@eslint/js": "^9.18.0", "@types/node": "^22.10.5", "@types/node-int64": "^0.4.29", - "@types/q": "^1.5.1", "buffer-equals": "^1.0.4", "commander": "^13.0.0", "connect": "^3.6.6", @@ -663,12 +661,6 @@ "@types/node": "*" } }, - "node_modules/@types/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz", - "integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==", - "dev": true - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -2662,15 +2654,6 @@ "node": ">=6" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", diff --git a/package.json b/package.json index febc130fe3f..492286b561a 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "browser-or-node": "^1.2.1", "isomorphic-ws": "^4.0.1", "node-int64": "^0.4.0", - "q": "^1.5.0", "uuid": "^14.0.0", "ws": "^5.2.3" }, @@ -47,7 +46,6 @@ "@eslint/js": "^9.18.0", "@types/node": "^22.10.5", "@types/node-int64": "^0.4.29", - "@types/q": "^1.5.1", "buffer-equals": "^1.0.4", "commander": "^13.0.0", "connect": "^3.6.6", From ba067fc89a5a3b63483099c5e2cb09b016ab71fa Mon Sep 17 00:00:00 2001 From: Jiayu Liu Date: Mon, 25 May 2026 08:34:58 +0800 Subject: [PATCH 2/3] Fix nodets ts-compile: drop unused second arg from test_driver fail() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Native Promise's `.catch(fn)` strictly expects a one-arg callback; Q's `.fail(fn)` was loose about extra args, so `function (error, response)` worked before. TS errors out with TS2345 — drop the unused `response` arg. Client: nodets Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/nodets/test/test_driver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nodets/test/test_driver.ts b/lib/nodets/test/test_driver.ts index c5e54a3f7b1..40b1efb49dd 100644 --- a/lib/nodets/test/test_driver.ts +++ b/lib/nodets/test/test_driver.ts @@ -132,7 +132,7 @@ export function ThriftTestDriverPromise( var checkRecursively = makeRecursiveCheck(assert); function fail(msg: string) { - return function (error, response) { + return function (error) { if (error !== null) { assert.fail(msg); } From 7c3f379922cee95db194cd8d86d9945ffcdccca4 Mon Sep 17 00:00:00 2001 From: Jiayu Liu Date: Mon, 25 May 2026 09:38:03 +0800 Subject: [PATCH 3/3] Address review feedback on Q-to-Promise migration - t_js_generator.cc: stop emitting an arrow function inside the Promise constructor for non-ES6 native_promise mode. Use `new Promise(function(resolve) { resolve(...); }.bind(this))` so the output stays ES5-compatible and `this` is bound explicitly. - test_driver.ts: pass the actual `fnName` variable to `.catch(fail(...))` in `makeAsserter` (was the literal string "fnName"), so failures report which RPC actually failed. - test_driver.ts: in the `testException("TException")` / "Xception" unexpected-resolve branches, call `assert.fail(...)` directly instead of `fail(...)` which only returned a function and was never invoked. Client: js,nodets Co-Authored-By: Claude Opus 4.7 (1M context) --- compiler/cpp/src/thrift/generate/t_js_generator.cc | 8 ++++++-- lib/nodets/test/test_driver.ts | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/cpp/src/thrift/generate/t_js_generator.cc b/compiler/cpp/src/thrift/generate/t_js_generator.cc index 944ecd10449..a42ced2e3cb 100644 --- a/compiler/cpp/src/thrift/generate/t_js_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_js_generator.cc @@ -1590,8 +1590,12 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* << ".length === " << fields.size() << ") {" << '\n'; indent_up(); - if (gen_es6_ || gen_native_promise_) { + if (gen_es6_) { indent(f_service_) << "new Promise((resolve) => resolve(this._handler." << tfunction->get_name() << ".bind(this._handler)(" << '\n'; + } else if (gen_native_promise_) { + // Non-ES6 native Promise: use function expression with explicit `this` + // binding so we don't rely on arrow-function lexical `this`. + indent(f_service_) << "new Promise(function(resolve) { resolve(this._handler." << tfunction->get_name() << ".bind(this._handler)(" << '\n'; } else { string maybeComma = (fields.size() > 0 ? "," : ""); indent(f_service_) << "Q.fcall(this._handler." << tfunction->get_name() << ".bind(this._handler)" @@ -1608,7 +1612,7 @@ void t_js_generator::generate_process_function(t_service* tservice, t_function* if (gen_es6_) { indent(f_service_) << "))).then(result => {" << '\n'; } else if (gen_native_promise_) { - indent(f_service_) << "))).then(function(result) {" << '\n'; + indent(f_service_) << ")); }.bind(this)).then(function(result) {" << '\n'; } else { indent(f_service_) << ").then(function(result) {" << '\n'; } diff --git a/lib/nodets/test/test_driver.ts b/lib/nodets/test/test_driver.ts index 40b1efb49dd..968e0168dd4 100644 --- a/lib/nodets/test/test_driver.ts +++ b/lib/nodets/test/test_driver.ts @@ -148,7 +148,7 @@ export function ThriftTestDriverPromise( .then(function (actual: any) { assertionFn(actual, expected, fnName); }) - .catch(fail("fnName")); + .catch(fail(fnName)); }; } @@ -180,7 +180,7 @@ export function ThriftTestDriverPromise( Promise.resolve(client.testException("TException")) .then(function (response) { - fail("testException: TException"); + assert.fail("testException: TException"); }) .catch(function (err) { assert.ok(err instanceof TException); @@ -188,7 +188,7 @@ export function ThriftTestDriverPromise( Promise.resolve(client.testException("Xception")) .then(function (response) { - fail("testException: Xception"); + assert.fail("testException: Xception"); }) .catch(function (err) { assert.ok(err instanceof ttypes.Xception);