From 5f512e0abfe7bfeb1c3e63c70c11604293617adb Mon Sep 17 00:00:00 2001 From: Hossam Hindawy Date: Thu, 9 Jul 2026 18:16:54 +0300 Subject: [PATCH 1/5] Support setting serviceUrl via setClientOptions --- spec/src/constructorio.js | 56 +++++++++++++++++++++++++++++++++++++++ src/constructorio.js | 6 ++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/spec/src/constructorio.js b/spec/src/constructorio.js index 4fdbcac2..93b812f8 100644 --- a/spec/src/constructorio.js +++ b/spec/src/constructorio.js @@ -715,6 +715,62 @@ describe(`ConstructorIO${bundledDescriptionSuffix}`, () => { expect(instance.recommendations.options).to.have.property('sessionId').to.equal(oldSessionId); expect(instance.tracker.options).to.have.property('sessionId').to.equal(oldSessionId); }); + + it('Should update the client options with a new service url', () => { + const newServiceUrl = 'https://new-service-url.cnstrc.com'; + const instance = new ConstructorIO({ apiKey: validApiKey }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + + instance.setClientOptions({ + serviceUrl: newServiceUrl, + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + }); + + it('Should update the options for modules with a new service url', () => { + const newServiceUrl = 'https://new-service-url.cnstrc.com'; + const instance = new ConstructorIO({ apiKey: validApiKey }); + + expect(instance.search.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.autocomplete.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.browse.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.recommendations.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + expect(instance.tracker.options).to.have.property('serviceUrl').to.equal('https://ac.cnstrc.com'); + + instance.setClientOptions({ + serviceUrl: newServiceUrl, + }); + + expect(instance.search.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.autocomplete.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.browse.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.recommendations.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + expect(instance.tracker.options).to.have.property('serviceUrl').to.equal(newServiceUrl); + }); + + it('Should not update the client options service url with a falsy value', () => { + const originalServiceUrl = 'https://custom-service-url.cnstrc.com'; + const instance = new ConstructorIO({ + apiKey: validApiKey, + serviceUrl: originalServiceUrl, + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(originalServiceUrl); + + instance.setClientOptions({ + serviceUrl: '', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(originalServiceUrl); + + instance.setClientOptions({ + serviceUrl: null, + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal(originalServiceUrl); + }); }); if (bundled) { diff --git a/src/constructorio.js b/src/constructorio.js index c4e8cb39..282e277e 100644 --- a/src/constructorio.js +++ b/src/constructorio.js @@ -180,7 +180,7 @@ class ConstructorIO { */ setClientOptions(options) { if (Object.keys(options).length) { - const { apiKey, segments, testCells, sessionId, userId, sendTrackingEvents } = options; + const { apiKey, segments, testCells, sessionId, userId, sendTrackingEvents, serviceUrl } = options; if (apiKey) { this.options.apiKey = apiKey; @@ -206,6 +206,10 @@ class ConstructorIO { if ('userId' in options) { this.options.userId = userId; } + + if (serviceUrl) { + this.options.serviceUrl = serviceUrl; + } } } } From 219dcd12282405b79e5071086e5d1f3822b9f4c6 Mon Sep 17 00:00:00 2001 From: Hossam Hindawy Date: Thu, 9 Jul 2026 18:18:30 +0300 Subject: [PATCH 2/5] Update cspell --- cspell.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cspell.json b/cspell.json index df8b430d..2653f568 100644 --- a/cspell.json +++ b/cspell.json @@ -50,6 +50,7 @@ "Bytespider", "Timespans", "googlequicksearchbox", - "cnstrc" + "cnstrc", + "iife" ] } From 1d24e3a84e6e98fe2cc8ee8efe2d3cfb17df79f6 Mon Sep 17 00:00:00 2001 From: Hossam Hindawy Date: Thu, 9 Jul 2026 18:29:34 +0300 Subject: [PATCH 3/5] Normalize serviceUrl --- spec/src/constructorio.js | 43 +++++++++++++++++++++++++++++++++++++++ src/constructorio.js | 6 +++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/spec/src/constructorio.js b/spec/src/constructorio.js index 93b812f8..989a794b 100644 --- a/spec/src/constructorio.js +++ b/spec/src/constructorio.js @@ -771,6 +771,49 @@ describe(`ConstructorIO${bundledDescriptionSuffix}`, () => { expect(instance.options).to.have.property('serviceUrl').to.equal(originalServiceUrl); }); + + it('Should prepend https to a service url without a protocol', () => { + const instance = new ConstructorIO({ apiKey: validApiKey }); + + instance.setClientOptions({ + serviceUrl: 'new-service-url.cnstrc.com', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://new-service-url.cnstrc.com'); + }); + + it('Should strip a trailing slash from the service url', () => { + const instance = new ConstructorIO({ apiKey: validApiKey }); + + instance.setClientOptions({ + serviceUrl: 'https://new-service-url.cnstrc.com/', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://new-service-url.cnstrc.com'); + }); + + it('Should upgrade an http service url to https by default', () => { + const instance = new ConstructorIO({ apiKey: validApiKey }); + + instance.setClientOptions({ + serviceUrl: 'http://new-service-url.cnstrc.com', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('https://new-service-url.cnstrc.com'); + }); + + it('Should preserve an http service url when allowHttpServiceUrl is set', () => { + const instance = new ConstructorIO({ + apiKey: validApiKey, + allowHttpServiceUrl: true, + }); + + instance.setClientOptions({ + serviceUrl: 'http://new-service-url.cnstrc.com', + }); + + expect(instance.options).to.have.property('serviceUrl').to.equal('http://new-service-url.cnstrc.com'); + }); }); if (bundled) { diff --git a/src/constructorio.js b/src/constructorio.js index 282e277e..4d419c4c 100644 --- a/src/constructorio.js +++ b/src/constructorio.js @@ -128,6 +128,7 @@ class ConstructorIO { apiKey, version: versionFromOptions || versionFromGlobal || computePackageVersion(), serviceUrl: helpers.addHTTPSToString(normalizedServiceUrl, allowHttpServiceUrl) || 'https://ac.cnstrc.com', + allowHttpServiceUrl, quizzesServiceUrl: (quizzesServiceUrl && quizzesServiceUrl.replace(/\/$/, '')) || 'https://quizzes.cnstrc.com', agentServiceUrl: (agentServiceUrl && agentServiceUrl.replace(/\/$/, '')) || 'https://agent.cnstrc.com', assistantServiceUrl: (assistantServiceUrl && assistantServiceUrl.replace(/\/$/, '')) || 'https://assistant.cnstrc.com', @@ -177,6 +178,7 @@ class ConstructorIO { * @param {number} [options.sessionId] - Session ID - Will only be set in DOM-less environments * @param {string} [options.userId] - User ID * @param {boolean} [options.sendTrackingEvents] - Indicates if tracking events should be dispatched + * @param {string} [options.serviceUrl] - API URL endpoint (normalized to include an HTTPS protocol and strip a trailing slash) */ setClientOptions(options) { if (Object.keys(options).length) { @@ -208,7 +210,9 @@ class ConstructorIO { } if (serviceUrl) { - this.options.serviceUrl = serviceUrl; + const normalizedServiceUrl = serviceUrl.replace(/\/$/, ''); + + this.options.serviceUrl = helpers.addHTTPSToString(normalizedServiceUrl, this.options.allowHttpServiceUrl) || this.options.serviceUrl; } } } From 61d90acfc2407748fd52bda1e166e384d66c21ed Mon Sep 17 00:00:00 2001 From: Hossam Hassan Hindawy <7ossam9063@gmail.com> Date: Thu, 9 Jul 2026 18:49:43 +0300 Subject: [PATCH 4/5] Guard string check Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/constructorio.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/constructorio.js b/src/constructorio.js index 4d419c4c..7572724c 100644 --- a/src/constructorio.js +++ b/src/constructorio.js @@ -209,10 +209,10 @@ class ConstructorIO { this.options.userId = userId; } - if (serviceUrl) { + if (typeof serviceUrl === 'string' && serviceUrl.length) { const normalizedServiceUrl = serviceUrl.replace(/\/$/, ''); - - this.options.serviceUrl = helpers.addHTTPSToString(normalizedServiceUrl, this.options.allowHttpServiceUrl) || this.options.serviceUrl; + const formattedServiceUrl = helpers.addHTTPSToString(normalizedServiceUrl, this.options.allowHttpServiceUrl); + this.options.serviceUrl = formattedServiceUrl || this.options.serviceUrl; } } } From 934cef54b829b6fdf61a4d247c4871495b72c1b0 Mon Sep 17 00:00:00 2001 From: mudaafi Date: Fri, 10 Jul 2026 01:18:01 +0800 Subject: [PATCH 5/5] fix bundled-esm-tests workflow --- .github/workflows/run-tests-bundled-esm.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-tests-bundled-esm.yml b/.github/workflows/run-tests-bundled-esm.yml index 134cc67f..d2f65117 100644 --- a/.github/workflows/run-tests-bundled-esm.yml +++ b/.github/workflows/run-tests-bundled-esm.yml @@ -25,5 +25,6 @@ jobs: run: npm run test:bundled:esm:parallel env: TEST_REQUEST_API_KEY: ${{ secrets.TEST_REQUEST_API_KEY }} + TEST_PIA_REQUEST_API_KEY: ${{ secrets.TEST_PIA_REQUEST_API_KEY }} TEST_MEDIA_REQUEST_API_KEY: ${{ secrets.TEST_MEDIA_REQUEST_API_KEY }} SKIP_NETWORK_TIMEOUT_TESTS: true