From ee59d5a90b2250a934a4988c3c5876099aa71627 Mon Sep 17 00:00:00 2001 From: Illia Date: Thu, 12 Mar 2026 10:59:46 +0200 Subject: [PATCH 1/9] Start.io - Create User ID submodule and improve adapter --- modules/.submodules.json | 1 + modules/startioBidAdapter.js | 21 +- modules/startioBidAdapter.md | 28 ++- modules/startioIdSystem.js | 115 +++++++++++ modules/startioIdSystem.md | 35 ++++ modules/userId/eids.md | 7 + modules/userId/userId.md | 3 + test/spec/modules/startioBidAdapter_spec.js | 94 +++++++++ test/spec/modules/startioIdSystem_spec.js | 211 ++++++++++++++++++++ 9 files changed, 510 insertions(+), 5 deletions(-) create mode 100644 modules/startioIdSystem.js create mode 100644 modules/startioIdSystem.md create mode 100644 test/spec/modules/startioIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index c6b0db32e78..55d6a5a7cfb 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -53,6 +53,7 @@ "quantcastIdSystem", "rewardedInterestIdSystem", "sharedIdSystem", + "startioIdSystem", "taboolaIdSystem", "tapadIdSystem", "teadsIdSystem", diff --git a/modules/startioBidAdapter.js b/modules/startioBidAdapter.js index fe1d10bf9a2..e5a24ea0861 100644 --- a/modules/startioBidAdapter.js +++ b/modules/startioBidAdapter.js @@ -1,13 +1,15 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; -import { logError, isFn, isPlainObject } from '../src/utils.js'; +import { logError, isFn, isPlainObject, formatQS } from '../src/utils.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { ortb25Translator } from '../libraries/ortb2.5Translator/translator.js'; +import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; const BIDDER_CODE = 'startio'; const METHOD = 'POST'; const GVLID = 1216; const ENDPOINT_URL = `https://pbc-rtb.startappnetwork.com/1.3/2.5/getbid?account=pbc`; +const IFRAME_URL = 'https://cs.startappnetwork.com/sync?p=m4b8b3y4'; const converter = ortbConverter({ imp(buildImp, bidRequest, context) { @@ -151,6 +153,23 @@ export const spec = { }, onSetTargeting: (bid) => { }, + + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + const syncs = []; + + if (syncOptions.iframeEnabled) { + const consentParams = getUserSyncParams(gdprConsent, uspConsent, gppConsent); + const queryString = formatQS(consentParams); + const queryParam = queryString ? `&${queryString}` : ''; + + syncs.push({ + type: 'iframe', + url: `${IFRAME_URL}${queryParam}` + }); + } + + return syncs; + } }; registerBidder(spec); diff --git a/modules/startioBidAdapter.md b/modules/startioBidAdapter.md index 172af1aeb4e..d74e0e64d33 100644 --- a/modules/startioBidAdapter.md +++ b/modules/startioBidAdapter.md @@ -25,7 +25,7 @@ var adUnits = [ bidder: 'startio', params: { // REQUIRED - Publisher Account ID - accountId: 'your-account-id', + publisherId: 'your-account-id', // OPTIONAL - Enable test ads testAdsEnabled: true @@ -58,7 +58,7 @@ var videoAdUnits = [ { bidder: 'startio', params: { - accountId: 'your-account-id', + publisherId: 'your-account-id', testAdsEnabled: true } } @@ -85,7 +85,7 @@ var nativeAdUnits = [ { bidder: 'startio', params: { - accountId: 'your-account-id', + publisherId: 'your-account-id', testAdsEnabled: true } } @@ -94,8 +94,28 @@ var nativeAdUnits = [ ]; ``` +### Prebid Params Enabling User Sync + +To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'startioId' + }], + filterSettings: { + iframe: { + bidders: ['startio'], + filter: 'include' + } + } + } +}); +``` + # Additional Notes - The adapter processes requests via OpenRTB 2.5 standards. -- Ensure that the `accountId` parameter is set correctly for your integration. +- Ensure that the `publisherId` parameter is set correctly for your integration. - Test ads can be enabled using `testAdsEnabled: true` during development. - The adapter supports multiple ad formats, allowing publishers to serve banners, native ads and instream video ads seamlessly. diff --git a/modules/startioIdSystem.js b/modules/startioIdSystem.js new file mode 100644 index 00000000000..1f598e4dbd3 --- /dev/null +++ b/modules/startioIdSystem.js @@ -0,0 +1,115 @@ +/** + * This module adds startio ID support to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/startioIdSystem + * @requires module:modules/userId + */ +import { logError, formatQS } from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; +import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; + +const MODULE_NAME = 'startioId'; +const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4'; + +const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); + +function getCachedId() { + let cachedId; + + if (storage.cookiesAreEnabled()) { + cachedId = storage.getCookie(MODULE_NAME); + } + + if (!cachedId && storage.hasLocalStorage()) { + const expirationStr = storage.getDataFromLocalStorage(`${MODULE_NAME}_exp`); + if (expirationStr) { + const expirationDate = new Date(expirationStr); + if (expirationDate > new Date()) { + cachedId = storage.getDataFromLocalStorage(MODULE_NAME); + } + } + } + + return cachedId || null; +} + +function storeId(id, expiresInDays) { + expiresInDays = expiresInDays || 90; + const expirationDate = new Date(Date.now() + expiresInDays * 24 * 60 * 60 * 1000).toUTCString(); + + if (storage.cookiesAreEnabled()) { + storage.setCookie(MODULE_NAME, id, expirationDate, 'None'); + } + + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(`${MODULE_NAME}_exp`, expirationDate); + storage.setDataInLocalStorage(MODULE_NAME, id); + } +} + +function fetchIdFromServer(callback, expiresInDays, consentData) { + const consentParams = getUserSyncParams( + consentData?.gdpr, + consentData?.usp, + consentData?.gpp + ); + const queryString = formatQS(consentParams); + const url = queryString ? `${DEFAULT_ENDPOINT}&${queryString}` : DEFAULT_ENDPOINT; + + const callbacks = { + success: response => { + let responseId; + try { + const responseObj = JSON.parse(response); + if (responseObj && responseObj.uid) { + responseId = responseObj.uid; + storeId(responseId, expiresInDays); + } else { + logError(`${MODULE_NAME}: Server response missing 'uid' field`); + } + } catch (error) { + logError(`${MODULE_NAME}: Error parsing server response`, error); + } + callback(responseId); + }, + error: error => { + logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); + } + }; + ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); +} + +export const startioIdSubmodule = { + name: MODULE_NAME, + decode(value) { + return value && typeof value === 'string' + ? { 'startioId': value } + : undefined; + }, + getId(config, consentData, storedId) { + if (storedId) { + return { id: storedId }; + } + + const cachedId = getCachedId(); + if (cachedId) { + return { id: cachedId }; + } + const storageConfig = config && config.storage; + const expiresInDays = storageConfig && storageConfig.expires; + return { callback: (cb) => fetchIdFromServer(cb, expiresInDays, consentData) }; + }, + + eids: { + 'startioId': { + source: 'start.io', + atype: 1 + }, + } +}; + +submodule('userId', startioIdSubmodule); diff --git a/modules/startioIdSystem.md b/modules/startioIdSystem.md new file mode 100644 index 00000000000..84bee91fa8f --- /dev/null +++ b/modules/startioIdSystem.md @@ -0,0 +1,35 @@ +## Start.io User ID Submodule + +The Start.io User ID submodule generates and persists a unique user identifier by fetching it from a publisher-supplied endpoint. The ID is stored in both cookies and local storage for subsequent page loads and is made available to other Prebid.js modules via the standard `eids` interface. + +For integration support, contact prebid@start.io. + +### Prebid Params Enabling User Sync + +To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup: + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'startioId' + }], + filterSettings: { + iframe: { + bidders: ['startio'], + filter: 'include' + } + } + } +}); +``` + +This configuration allows Start.io to sync user data via iframe, which is necessary for cross-domain user identification. + +## Parameter Descriptions for the `userSync` Configuration Section + +The below parameters apply only to the Start.io User ID integration. + +| Param under userSync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| name | Required | String | The name of this module. | `"startioId"` | diff --git a/modules/userId/eids.md b/modules/userId/eids.md index bdd8a0bb3e8..b2fb808bafc 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -350,6 +350,13 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 1 }] + }, + { + source: 'start.io', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] } ] ``` diff --git a/modules/userId/userId.md b/modules/userId/userId.md index f7bea8fd9f8..f59f01f00cb 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -167,6 +167,9 @@ pbjs.setConfig({ }, { name: "mygaruId" + }, + { + name: "startioId" } ], syncDelay: 5000, diff --git a/test/spec/modules/startioBidAdapter_spec.js b/test/spec/modules/startioBidAdapter_spec.js index 2b7269997aa..1541007cc74 100644 --- a/test/spec/modules/startioBidAdapter_spec.js +++ b/test/spec/modules/startioBidAdapter_spec.js @@ -370,4 +370,98 @@ describe('Prebid Adapter: Startio', function () { }); } }); + + describe('getUserSyncs', function () { + it('should return an iframe sync when iframeEnabled is true', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, []); + + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.be.a('string'); + }); + + it('should return an empty array when iframeEnabled is false', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: false }, []); + + expect(syncs).to.have.lengthOf(0); + }); + + it('should return an empty array when syncOptions is empty', function () { + const syncs = spec.getUserSyncs({}, []); + + expect(syncs).to.have.lengthOf(0); + }); + + it('should append GDPR consent params to the sync URL', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==' + }; + + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); + + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.include('gdpr_consent=BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + }); + + it('should append gdpr=0 when gdprApplies is false', function () { + const gdprConsent = { + gdprApplies: false, + consentString: '' + }; + + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); + + expect(syncs[0].url).to.include('gdpr=0'); + }); + + it('should append USP consent param to the sync URL', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, '1YNN'); + + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].url).to.include('us_privacy=1YNN'); + }); + + it('should append GPP consent params to the sync URL', function () { + const gppConsent = { + gppString: 'DBABMA~BAAAAAAAAgA.QA', + applicableSections: [7, 8] + }; + + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined, gppConsent); + + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].url).to.include('gpp=DBABMA~BAAAAAAAAgA.QA'); + expect(syncs[0].url).to.include('gpp_sid=7,8'); + }); + + it('should append all consent params together when all are provided', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'testConsent' + }; + const uspConsent = '1YNN'; + const gppConsent = { + gppString: 'testGpp', + applicableSections: [2] + }; + + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent, uspConsent, gppConsent); + + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.include('gdpr_consent=testConsent'); + expect(syncs[0].url).to.include('us_privacy=1YNN'); + expect(syncs[0].url).to.include('gpp=testGpp'); + expect(syncs[0].url).to.include('gpp_sid=2'); + }); + + it('should not append query string when no consent params are provided', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, []); + + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].url).to.equal('https://cs.startappnetwork.com/sync?p=m4b8b3y4'); + }); + }); }); diff --git a/test/spec/modules/startioIdSystem_spec.js b/test/spec/modules/startioIdSystem_spec.js new file mode 100644 index 00000000000..25d3d52a518 --- /dev/null +++ b/test/spec/modules/startioIdSystem_spec.js @@ -0,0 +1,211 @@ +import * as utils from '../../../src/utils.js'; +import { server } from 'test/mocks/xhr.js'; +import { startioIdSubmodule } from 'modules/startioIdSystem.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { getStorageManager } from '../../../src/storageManager.js'; + +describe('StartIO ID System', function () { + let sandbox; + let storage; + + const validConfig = { + params: {}, + storage: { + expires: 365 + } + }; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + sandbox.stub(utils, 'logError'); + storage = getStorageManager({ moduleType: 'userId', moduleName: 'startioId' }); + + // Clear any cached storage + if (storage.cookiesAreEnabled()) { + storage.setCookie('startioId', '', new Date(0).toUTCString()); + } + if (storage.hasLocalStorage()) { + storage.removeDataFromLocalStorage('startioId'); + storage.removeDataFromLocalStorage('startioId_exp'); + } + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('module registration', function () { + it('should register the submodule', function () { + expect(startioIdSubmodule.name).to.equal('startioId'); + }); + + it('should have eids configuration', function () { + expect(startioIdSubmodule.eids).to.deep.equal({ + 'startioId': { + source: 'start.io', + atype: 1 + } + }); + }); + }); + + describe('decode', function () { + it('should return undefined if no value passed', function () { + expect(startioIdSubmodule.decode()).to.be.undefined; + }); + + it('should return undefined if invalid value passed', function () { + expect(startioIdSubmodule.decode(123)).to.be.undefined; + expect(startioIdSubmodule.decode(null)).to.be.undefined; + expect(startioIdSubmodule.decode({})).to.be.undefined; + expect(startioIdSubmodule.decode('')).to.be.undefined; + }); + + it('should return startioId object if valid string passed', function () { + const id = 'test-uuid-12345'; + const result = startioIdSubmodule.decode(id); + expect(result).to.deep.equal({ 'startioId': id }); + }); + }); + + describe('eid', function () { + it('should generate correct EID', function () { + const TEST_UID = 'test-uid-value'; + const eids = createEidsArray(startioIdSubmodule.decode(TEST_UID), new Map(Object.entries(startioIdSubmodule.eids))); + expect(eids).to.eql([ + { + source: 'start.io', + uids: [ + { + atype: 1, + id: TEST_UID + } + ] + } + ]); + }); + }); + + describe('getId', function () { + it('should return callback and fire ajax even if no endpoint configured', function () { + const config = { params: {} }; + const result = startioIdSubmodule.getId(config); + expect(result).to.have.property('callback'); + expect(typeof result.callback).to.equal('function'); + + const callbackSpy = sinon.spy(); + result.callback(callbackSpy); + expect(server.requests.length).to.equal(1); + }); + + it('should return callback and fire ajax even if endpoint is not a string', function () { + const config = { params: { endpoint: 123 } }; + const result = startioIdSubmodule.getId(config); + expect(result).to.have.property('callback'); + expect(typeof result.callback).to.equal('function'); + + const callbackSpy = sinon.spy(); + result.callback(callbackSpy); + expect(server.requests.length).to.equal(1); + }); + + it('should return existing storedId immediately if provided', function () { + const storedId = 'existing-id-12345'; + const result = startioIdSubmodule.getId(validConfig, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + expect(server.requests.length).to.eq(0); + }); + + it('should fetch new ID from server if no storedId provided', function () { + const result = startioIdSubmodule.getId(validConfig); + expect(result).to.have.property('callback'); + expect(typeof result.callback).to.equal('function'); + }); + + it('should invoke callback with ID from server response', function () { + const callbackSpy = sinon.spy(); + const callback = startioIdSubmodule.getId(validConfig).callback; + callback(callbackSpy); + + const request = server.requests[0]; + expect(request.method).to.eq('GET'); + expect(request.url).to.eq('https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4'); + + const serverId = 'new-server-id-12345'; + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ uid: serverId })); + + expect(callbackSpy.calledOnce).to.be.true; + expect(callbackSpy.lastCall.lastArg).to.equal(serverId); + }); + + it('should append consent params to the request URL', function () { + const consentData = { + gdpr: { + gdprApplies: true, + consentString: 'TEST_CONSENT_STRING' + }, + usp: '1YNN', + gpp: { + gppString: 'TEST_GPP_STRING', + applicableSections: [7] + } + }; + + const callbackSpy = sinon.spy(); + const callback = startioIdSubmodule.getId(validConfig, consentData).callback; + callback(callbackSpy); + + const request = server.requests[0]; + expect(request.url).to.include('gdpr=1'); + expect(request.url).to.include('gdpr_consent=TEST_CONSENT_STRING'); + expect(request.url).to.include('us_privacy=1YNN'); + expect(request.url).to.include('gpp=TEST_GPP_STRING'); + expect(request.url).to.include('gpp_sid=7'); + }); + + it('should send request without consent params when consentData is absent', function () { + const callbackSpy = sinon.spy(); + const callback = startioIdSubmodule.getId(validConfig).callback; + callback(callbackSpy); + + const request = server.requests[0]; + expect(request.url).to.eq('https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4'); + }); + + it('should log error if server response is missing uid field', function () { + const callbackSpy = sinon.spy(); + const callback = startioIdSubmodule.getId(validConfig).callback; + callback(callbackSpy); + + const request = server.requests[0]; + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ wrongField: 'value' })); + + expect(utils.logError.calledOnce).to.be.true; + expect(utils.logError.args[0][0]).to.include('missing \'uid\' field'); + }); + + it('should log error if server response is invalid JSON', function () { + const callbackSpy = sinon.spy(); + const callback = startioIdSubmodule.getId(validConfig).callback; + callback(callbackSpy); + + const request = server.requests[0]; + request.respond(200, { 'Content-Type': 'application/json' }, 'invalid-json{'); + + expect(utils.logError.calledOnce).to.be.true; + expect(utils.logError.args[0][0]).to.include('Error parsing'); + }); + + it('should log error if server request fails', function () { + const callbackSpy = sinon.spy(); + const callback = startioIdSubmodule.getId(validConfig).callback; + callback(callbackSpy); + + const request = server.requests[0]; + request.error(); + + expect(utils.logError.calledOnce).to.be.true; + expect(utils.logError.args[0][0]).to.include('encountered an error'); + }); + }); +}); From c2e83febbf25412f176bd4cfc7c505b359639130 Mon Sep 17 00:00:00 2001 From: Illia Date: Thu, 12 Mar 2026 13:06:45 +0200 Subject: [PATCH 2/9] Update modules/startioIdSystem.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- modules/startioIdSystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/startioIdSystem.md b/modules/startioIdSystem.md index 84bee91fa8f..89c30f34b70 100644 --- a/modules/startioIdSystem.md +++ b/modules/startioIdSystem.md @@ -1,6 +1,6 @@ ## Start.io User ID Submodule -The Start.io User ID submodule generates and persists a unique user identifier by fetching it from a publisher-supplied endpoint. The ID is stored in both cookies and local storage for subsequent page loads and is made available to other Prebid.js modules via the standard `eids` interface. +The Start.io User ID submodule generates and persists a unique user identifier by fetching it from a Start.io-managed endpoint. This endpoint is fixed within the submodule implementation and is not configurable via Prebid.js parameters. The ID is stored in both cookies and local storage for subsequent page loads and is made available to other Prebid.js modules via the standard `eids` interface. For integration support, contact prebid@start.io. From 34c0b4ea8f6e3e284e1a3f7d62684869f658b3aa Mon Sep 17 00:00:00 2001 From: illiamilshtein Date: Tue, 17 Mar 2026 13:56:37 +0200 Subject: [PATCH 3/9] Fix: Adjust spacing formatting in getStorageManager parameters --- modules/startioIdSystem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/startioIdSystem.js b/modules/startioIdSystem.js index 1f598e4dbd3..aad91368af5 100644 --- a/modules/startioIdSystem.js +++ b/modules/startioIdSystem.js @@ -14,7 +14,7 @@ import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; const MODULE_NAME = 'startioId'; const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4'; -const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function getCachedId() { let cachedId; From 6f37ca9426e52c8e6c82dea070330dbb8022b39c Mon Sep 17 00:00:00 2001 From: Illia Date: Tue, 17 Mar 2026 13:58:55 +0200 Subject: [PATCH 4/9] Fix: Adjust spacing formatting in getStorageManager parameters (#2) --- modules/startioIdSystem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/startioIdSystem.js b/modules/startioIdSystem.js index 1f598e4dbd3..aad91368af5 100644 --- a/modules/startioIdSystem.js +++ b/modules/startioIdSystem.js @@ -14,7 +14,7 @@ import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; const MODULE_NAME = 'startioId'; const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4'; -const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function getCachedId() { let cachedId; From 28c51c15ce6c520caf42e1f9a4be73831bc86bf2 Mon Sep 17 00:00:00 2001 From: illiamilshtein Date: Wed, 18 Mar 2026 17:05:45 +0200 Subject: [PATCH 5/9] Refactor: Remove unused storage management logic from StartIO ID module and set default storage expiration --- modules/startioIdSystem.js | 51 ++--------------------- test/spec/modules/startioIdSystem_spec.js | 24 +++++------ 2 files changed, 16 insertions(+), 59 deletions(-) diff --git a/modules/startioIdSystem.js b/modules/startioIdSystem.js index aad91368af5..eca40919812 100644 --- a/modules/startioIdSystem.js +++ b/modules/startioIdSystem.js @@ -7,50 +7,12 @@ import { logError, formatQS } from '../src/utils.js'; import { submodule } from '../src/hook.js'; import { ajax } from '../src/ajax.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { MODULE_TYPE_UID } from '../src/activities/modules.js'; import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; const MODULE_NAME = 'startioId'; const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4'; -const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); - -function getCachedId() { - let cachedId; - - if (storage.cookiesAreEnabled()) { - cachedId = storage.getCookie(MODULE_NAME); - } - - if (!cachedId && storage.hasLocalStorage()) { - const expirationStr = storage.getDataFromLocalStorage(`${MODULE_NAME}_exp`); - if (expirationStr) { - const expirationDate = new Date(expirationStr); - if (expirationDate > new Date()) { - cachedId = storage.getDataFromLocalStorage(MODULE_NAME); - } - } - } - - return cachedId || null; -} - -function storeId(id, expiresInDays) { - expiresInDays = expiresInDays || 90; - const expirationDate = new Date(Date.now() + expiresInDays * 24 * 60 * 60 * 1000).toUTCString(); - - if (storage.cookiesAreEnabled()) { - storage.setCookie(MODULE_NAME, id, expirationDate, 'None'); - } - - if (storage.hasLocalStorage()) { - storage.setDataInLocalStorage(`${MODULE_NAME}_exp`, expirationDate); - storage.setDataInLocalStorage(MODULE_NAME, id); - } -} - -function fetchIdFromServer(callback, expiresInDays, consentData) { +function fetchIdFromServer(callback, consentData) { const consentParams = getUserSyncParams( consentData?.gdpr, consentData?.usp, @@ -66,7 +28,6 @@ function fetchIdFromServer(callback, expiresInDays, consentData) { const responseObj = JSON.parse(response); if (responseObj && responseObj.uid) { responseId = responseObj.uid; - storeId(responseId, expiresInDays); } else { logError(`${MODULE_NAME}: Server response missing 'uid' field`); } @@ -94,14 +55,10 @@ export const startioIdSubmodule = { if (storedId) { return { id: storedId }; } - - const cachedId = getCachedId(); - if (cachedId) { - return { id: cachedId }; + if (config.storage && config.storage.expires == null) { + config.storage.expires = 90; } - const storageConfig = config && config.storage; - const expiresInDays = storageConfig && storageConfig.expires; - return { callback: (cb) => fetchIdFromServer(cb, expiresInDays, consentData) }; + return { callback: (cb) => fetchIdFromServer(cb, consentData) }; }, eids: { diff --git a/test/spec/modules/startioIdSystem_spec.js b/test/spec/modules/startioIdSystem_spec.js index 25d3d52a518..ec2bc487139 100644 --- a/test/spec/modules/startioIdSystem_spec.js +++ b/test/spec/modules/startioIdSystem_spec.js @@ -2,11 +2,9 @@ import * as utils from '../../../src/utils.js'; import { server } from 'test/mocks/xhr.js'; import { startioIdSubmodule } from 'modules/startioIdSystem.js'; import { createEidsArray } from '../../../modules/userId/eids.js'; -import { getStorageManager } from '../../../src/storageManager.js'; describe('StartIO ID System', function () { let sandbox; - let storage; const validConfig = { params: {}, @@ -18,16 +16,6 @@ describe('StartIO ID System', function () { beforeEach(function () { sandbox = sinon.createSandbox(); sandbox.stub(utils, 'logError'); - storage = getStorageManager({ moduleType: 'userId', moduleName: 'startioId' }); - - // Clear any cached storage - if (storage.cookiesAreEnabled()) { - storage.setCookie('startioId', '', new Date(0).toUTCString()); - } - if (storage.hasLocalStorage()) { - storage.removeDataFromLocalStorage('startioId'); - storage.removeDataFromLocalStorage('startioId_exp'); - } }); afterEach(function () { @@ -207,5 +195,17 @@ describe('StartIO ID System', function () { expect(utils.logError.calledOnce).to.be.true; expect(utils.logError.args[0][0]).to.include('encountered an error'); }); + + it('should set default storage.expires to 90 when not provided', function () { + const config = { params: {}, storage: { type: 'html5', name: 'startioId' } }; + startioIdSubmodule.getId(config); + expect(config.storage.expires).to.equal(90); + }); + + it('should not override storage.expires when already set', function () { + const config = { params: {}, storage: { type: 'html5', name: 'startioId', expires: 365 } }; + startioIdSubmodule.getId(config); + expect(config.storage.expires).to.equal(365); + }); }); }); From e72fd1be7a3b92bfb510e8052728fc31b3e2b3cc Mon Sep 17 00:00:00 2001 From: illiamilshtein Date: Wed, 18 Mar 2026 17:18:55 +0200 Subject: [PATCH 6/9] Refactor: Remove unused storage management logic from StartIO ID module and set default storage expiration --- modules/startioIdSystem.js | 2 ++ test/spec/modules/startioIdSystem_spec.js | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/modules/startioIdSystem.js b/modules/startioIdSystem.js index eca40919812..637d69a65a3 100644 --- a/modules/startioIdSystem.js +++ b/modules/startioIdSystem.js @@ -10,6 +10,7 @@ import { ajax } from '../src/ajax.js'; import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; const MODULE_NAME = 'startioId'; +const GVLID = 1216; const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4'; function fetchIdFromServer(callback, consentData) { @@ -46,6 +47,7 @@ function fetchIdFromServer(callback, consentData) { export const startioIdSubmodule = { name: MODULE_NAME, + gvlid: GVLID, decode(value) { return value && typeof value === 'string' ? { 'startioId': value } diff --git a/test/spec/modules/startioIdSystem_spec.js b/test/spec/modules/startioIdSystem_spec.js index ec2bc487139..dbdc2096bbf 100644 --- a/test/spec/modules/startioIdSystem_spec.js +++ b/test/spec/modules/startioIdSystem_spec.js @@ -27,6 +27,10 @@ describe('StartIO ID System', function () { expect(startioIdSubmodule.name).to.equal('startioId'); }); + it('should have gvlid', function () { + expect(startioIdSubmodule.gvlid).to.equal(1216); + }); + it('should have eids configuration', function () { expect(startioIdSubmodule.eids).to.deep.equal({ 'startioId': { From 42fa66fe8c766cc3c860d0ec2cc7bd8566413aa4 Mon Sep 17 00:00:00 2001 From: illiamilshtein Date: Thu, 19 Mar 2026 11:09:26 +0200 Subject: [PATCH 7/9] Enhance: Add configurable storage options and expiration to StartIO ID module --- modules/startioIdSystem.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/startioIdSystem.md b/modules/startioIdSystem.md index 89c30f34b70..0fd02f8bebc 100644 --- a/modules/startioIdSystem.md +++ b/modules/startioIdSystem.md @@ -12,7 +12,12 @@ To enable iframe-based user syncing for Start.io, include the `filterSettings` c pbjs.setConfig({ userSync: { userIds: [{ - name: 'startioId' + name: 'startioId', + storage: { + type: 'cookie&html5', // 'cookie', 'html5', or 'cookie&html5' + name: 'startioId', + expires: 365 // optional, 90 days by default + } }], filterSettings: { iframe: { From b71da0ebd481399784cf05e2bd755f537eb5040b Mon Sep 17 00:00:00 2001 From: illiamilshtein Date: Thu, 19 Mar 2026 11:13:06 +0200 Subject: [PATCH 8/9] Docs: Update StartIO ID module documentation with storage configuration details --- modules/startioIdSystem.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/startioIdSystem.md b/modules/startioIdSystem.md index 0fd02f8bebc..fc2fb1f0ee4 100644 --- a/modules/startioIdSystem.md +++ b/modules/startioIdSystem.md @@ -38,3 +38,7 @@ The below parameters apply only to the Start.io User ID integration. | Param under userSync.userIds[] | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | | name | Required | String | The name of this module. | `"startioId"` | +| storage | Required | Object | Storage configuration for the user ID. | | +| storage.type | Required | String | Type of storage: `"cookie"`, `"html5"`, or `"cookie&html5"`. | `"cookie&html5"` | +| storage.name | Required | String | The name used to store the user ID. | `"startioId"` | +| storage.expires | Optional | Number | Number of days before the stored ID expires. Defaults to `90`. | `365` | From 95ae68625352f9604dd5964b9d1bdc94ba3dd0fe Mon Sep 17 00:00:00 2001 From: illiamilshtein Date: Thu, 19 Mar 2026 14:24:56 +0200 Subject: [PATCH 9/9] Docs: Update StartIO ID module documentation with storage configuration details --- modules/startioBidAdapter.md | 6 +++++- modules/startioIdSystem.md | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/startioBidAdapter.md b/modules/startioBidAdapter.md index d74e0e64d33..b727acd3195 100644 --- a/modules/startioBidAdapter.md +++ b/modules/startioBidAdapter.md @@ -102,7 +102,11 @@ To enable iframe-based user syncing for Start.io, include the `filterSettings` c pbjs.setConfig({ userSync: { userIds: [{ - name: 'startioId' + name: 'startioId', + storage: { + type: 'cookie&html5', + name: 'startioId' + } }], filterSettings: { iframe: { diff --git a/modules/startioIdSystem.md b/modules/startioIdSystem.md index fc2fb1f0ee4..5c3de1bf961 100644 --- a/modules/startioIdSystem.md +++ b/modules/startioIdSystem.md @@ -16,7 +16,7 @@ pbjs.setConfig({ storage: { type: 'cookie&html5', // 'cookie', 'html5', or 'cookie&html5' name: 'startioId', - expires: 365 // optional, 90 days by default + expires: 90 // optional, 90 days by default } }], filterSettings: {