From eece80518eb71c9aafad163993211ce5a940e59a Mon Sep 17 00:00:00 2001 From: "Mr.Online" Date: Mon, 17 Nov 2025 15:55:34 +0530 Subject: [PATCH] fix: upgrade got to latest version --- .gitignore | 1 + index.js | 101 +++++++++++++++++++++++++++++-------------- package.json | 4 +- test/shopify.test.js | 37 +++++++++++----- 4 files changed, 99 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 25fbf5a1..c6f71ff1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ coverage/ +.idea/ diff --git a/index.js b/index.js index 12acaade..92f695c3 100644 --- a/index.js +++ b/index.js @@ -3,12 +3,41 @@ const transform = require('lodash/transform'); const EventEmitter = require('events'); const stopcock = require('stopcock'); -const got = require('got'); const url = require('url'); const pkg = require('./package'); const resources = require('./resources'); +// got is now ESM-only, so we need to use dynamic import +// We lazy load it only when needed (when a request is made) +let got; +let gotPromise; + +/** + * Ensures got is loaded before use + * @private + * @return {Promise} + */ +function ensureGot() { + if (!gotPromise) { + gotPromise = import('got').then((module) => { + got = module.default; + return got; + }); + } + return gotPromise; +} + +/** + * Converts a URL object to a string URL for got v12+ + * @param {Object} urlObj URL object with pathname, hostname, protocol, search, etc. + * @return {String} Full URL string + * @private + */ +function urlObjToString(urlObj) { + return url.format(urlObj); +} + const retryableErrorCodes = new Set([ 'ETIMEDOUT', 'ECONNRESET', @@ -155,7 +184,7 @@ Shopify.prototype.request = function request(uri, method, key, data, headers) { parseJson: this.options.parseJson, responseType: 'json', stringifyJson: this.options.stringifyJson, - timeout: this.options.timeout + timeout: { request: this.options.timeout } }; const afterResponse = (res) => { @@ -189,45 +218,49 @@ Shopify.prototype.request = function request(uri, method, key, data, headers) { statusCodes: retryableStatusCodesArray }; } else { - options.retry = 0; + options.retry = { limit: 0 }; } - return got(uri, options).then((res) => { - const body = res.body; - - if (res.statusCode === 202 && res.headers['location']) { - const retryAfter = res.headers['retry-after'] * 1000 || 0; - const { pathname, search } = url.parse(res.headers['location']); + const urlString = urlObjToString(uri); - return delay(retryAfter).then(() => { - const uri = { pathname, ...this.baseUrl }; + return ensureGot() + .then(() => got(urlString, options)) + .then((res) => { + const body = res.body; - if (search) uri.search = search; + if (res.statusCode === 202 && res.headers['location']) { + const retryAfter = res.headers['retry-after'] * 1000 || 0; + const { pathname, search } = url.parse(res.headers['location']); - return this.request(uri, 'GET', key); - }); - } - - const data = key ? body[key] : body || {}; + return delay(retryAfter).then(() => { + const uri = { pathname, ...this.baseUrl }; - if (res.headers.link) { - const link = parseLinkHeader(res.headers.link); + if (search) uri.search = search; - if (link.next) { - Object.defineProperties(data, { - nextPageParameters: { value: link.next.query } + return this.request(uri, 'GET', key); }); } - if (link.previous) { - Object.defineProperties(data, { - previousPageParameters: { value: link.previous.query } - }); + const data = key ? body[key] : body || {}; + + if (res.headers.link) { + const link = parseLinkHeader(res.headers.link); + + if (link.next) { + Object.defineProperties(data, { + nextPageParameters: { value: link.next.query } + }); + } + + if (link.previous) { + Object.defineProperties(data, { + previousPageParameters: { value: link.previous.query } + }); + } } - } - return data; - }); + return data; + }); }; /** @@ -283,7 +316,7 @@ Shopify.prototype.graphql = function graphql(data, variables) { method: 'POST', parseJson: this.options.parseJson, responseType: 'json', - timeout: this.options.timeout + timeout: { request: this.options.timeout } }; const updateGqlLimits = (res) => { @@ -334,10 +367,14 @@ Shopify.prototype.graphql = function graphql(data, variables) { statusCodes: retryableStatusCodesArray }; } else { - options.retry = 0; + options.retry = { limit: 0 }; } - return got(uri, options).then(responseData); + const urlString = urlObjToString(uri); + + return ensureGot() + .then(() => got(urlString, options)) + .then(responseData); }; resources.registerAll(Shopify); diff --git a/package.json b/package.json index bbd540e7..80d79946 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "test": "test" }, "engines": { - "node": ">=10.0.0" + "node": ">=20.0.0" }, "files": [ "resources", @@ -17,7 +17,7 @@ "types/index.d.ts" ], "dependencies": { - "got": "^11.1.4", + "got": "^14.6.4", "lodash": "^4.17.10", "qs": "^6.5.2", "stopcock": "^1.0.0" diff --git a/test/shopify.test.js b/test/shopify.test.js index 5a0a9bed..fb94522b 100644 --- a/test/shopify.test.js +++ b/test/shopify.test.js @@ -5,7 +5,6 @@ describe('Shopify', () => { const expect = require('chai').expect; const format = require('url').format; const nock = require('nock'); - const got = require('got'); const qs = require('qs'); const Blog = require('../resources/blog'); @@ -25,6 +24,18 @@ describe('Shopify', () => { const shopifyWithRetries = common.shopifyWithRetries; const shopName = common.shopName; + // got is now ESM-only, so we need to use dynamic import + let got; + before(async () => { + const gotModule = await import('got'); + got = gotModule.default; + // Attach error classes to got for backward compatibility in tests + got.RequestError = gotModule.RequestError; + got.TimeoutError = gotModule.TimeoutError; + got.ParseError = gotModule.ParseError; + got.HTTPError = gotModule.HTTPError; + }); + it('exports the constructor', () => { expect(Shopify).to.be.a('function'); }); @@ -207,7 +218,9 @@ describe('Shopify', () => { }, (err) => { expect(err).to.be.an.instanceof(got.HTTPError); - expect(err.message).to.equal('Response code 400 (Bad Request)'); + expect(err.message).to.include( + 'Request failed with status code 400 (Bad Request)' + ); } ); }); @@ -597,7 +610,9 @@ describe('Shopify', () => { return shopifyWithRetries.product.get(10).catch((err) => { expect(err).to.be.an.instanceof(got.HTTPError); - expect(err.message).to.equal('Response code 404 (Not Found)'); + expect(err.message).to.include( + 'Request failed with status code 404 (Not Found)' + ); }); }); @@ -608,8 +623,8 @@ describe('Shopify', () => { return shopifyWithRetries.product.update(10).catch((err) => { expect(err).to.be.an.instanceof(got.HTTPError); - expect(err.message).to.equal( - 'Response code 422 (Unprocessable Entity)' + expect(err.message).to.include( + 'Request failed with status code 422 (Unprocessable Entity)' ); }); }); @@ -621,8 +636,8 @@ describe('Shopify', () => { return shopifyWithRetries.product.update(10).catch((err) => { expect(err).to.be.an.instanceof(got.HTTPError); - expect(err.message).to.equal( - 'Response code 422 (Unprocessable Entity)' + expect(err.message).to.include( + 'Request failed with status code 422 (Unprocessable Entity)' ); }); }); @@ -636,8 +651,8 @@ describe('Shopify', () => { return shopifyWithRetries.product.update(10).catch((err) => { expect(err).to.be.an.instanceof(got.HTTPError); - expect(err.message).to.equal( - 'Response code 422 (Unprocessable Entity)' + expect(err.message).to.include( + 'Request failed with status code 422 (Unprocessable Entity)' ); }); }); @@ -912,7 +927,9 @@ describe('Shopify', () => { }, (err) => { expect(err).to.be.an.instanceof(got.HTTPError); - expect(err.message).to.equal('Response code 400 (Bad Request)'); + expect(err.message).to.include( + 'Request failed with status code 400 (Bad Request)' + ); } ); });