• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / #298

29 May 2025 11:21AM UTC coverage: 82.464% (-7.7%) from 90.144%
#298

push

travis-ci

prebidjs-release
Prebid 9.45.0 release

12606 of 17518 branches covered (71.96%)

18622 of 22582 relevant lines covered (82.46%)

157.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.13
/modules/sharedIdSystem.js
1
/**
1✔
2
 * This module adds SharedId to the User ID module
3
 * The {@link module:modules/userId} module is required
4
 * @module modules/sharedIdSystem
5
 * @requires module:modules/userId
6
 */
7

8
import {parseUrl, buildUrl, triggerPixel, logInfo, hasDeviceAccess, generateUUID} from '../src/utils.js';
9
import {submodule} from '../src/hook.js';
10
import {getStorageManager} from '../src/storageManager.js';
11
import {VENDORLESS_GVLID} from '../src/consentHandler.js';
12
import {MODULE_TYPE_UID} from '../src/activities/modules.js';
13
import {domainOverrideToRootDomain} from '../libraries/domainOverrideToRootDomain/index.js';
14

15
/**
16
 * @typedef {import('../modules/userId/index.js').Submodule} Submodule
17
 * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig
18
 * @typedef {import('../modules/userId/index.js').SubmoduleParams} SubmoduleParams
19
 * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData
20
 * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
21
 */
22

23
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'sharedId'});
1✔
24
const COOKIE = 'cookie';
1✔
25
const LOCAL_STORAGE = 'html5';
1✔
26
const OPTOUT_NAME = '_pubcid_optout';
1✔
27
const PUB_COMMON_ID = 'PublisherCommonId';
1✔
28

29
/**
30
 * Read a value either from cookie or local storage
31
 * @param {string} name Name of the item
32
 * @param {string} type storage type override
33
 * @returns {string|null} a string if item exists
34
 */
35
function readValue(name, type) {
36
  if (type === COOKIE) {
96✔
37
    return storage.getCookie(name);
48✔
38
  } else if (type === LOCAL_STORAGE) {
48!
39
    if (storage.hasLocalStorage()) {
48!
40
      const expValue = storage.getDataFromLocalStorage(`${name}_exp`);
48✔
41
      if (!expValue) {
48!
42
        return storage.getDataFromLocalStorage(name);
48✔
43
      } else if ((new Date(expValue)).getTime() - Date.now() > 0) {
×
44
        return storage.getDataFromLocalStorage(name)
×
45
      }
46
    }
47
  }
48
}
49

50
function getIdCallback(pubcid, pixelUrl) {
51
  return function (callback, getStoredId) {
11✔
52
    if (pixelUrl) {
11✔
53
      queuePixelCallback(pixelUrl, pubcid, () => {
1✔
54
        callback(getStoredId() || pubcid);
×
55
      })();
56
    } else {
57
      callback(pubcid);
10✔
58
    }
59
  }
60
}
61

62
function queuePixelCallback(pixelUrl, id = '', callback) {
×
63
  if (!pixelUrl) {
1!
64
    return;
×
65
  }
66

67
  // Use pubcid as a cache buster
68
  const urlInfo = parseUrl(pixelUrl);
1✔
69
  urlInfo.search.id = encodeURIComponent('pubcid:' + id);
1✔
70
  const targetUrl = buildUrl(urlInfo);
1✔
71

72
  return function () {
1✔
73
    triggerPixel(targetUrl, callback);
1✔
74
  };
75
}
76

77
function hasOptedOut() {
78
  return !!((storage.cookiesAreEnabled() && readValue(OPTOUT_NAME, COOKIE)) ||
48✔
79
    (storage.hasLocalStorage() && readValue(OPTOUT_NAME, LOCAL_STORAGE)));
80
}
81

82
export const sharedIdSystemSubmodule = {
1✔
83
  /**
84
   * used to link submodule with config
85
   * @type {string}
86
   */
87
  name: 'sharedId',
88
  aliasName: 'pubCommonId',
89
  gvlid: VENDORLESS_GVLID,
90

91
  /**
92
   * decode the stored id value for passing to bid requests
93
   * @function
94
   * @param {string} value
95
   * @param {SubmoduleConfig} config
96
   * @returns {{pubcid:string}}
97
   */
98
  decode(value, config) {
99
    if (hasOptedOut()) {
28!
100
      logInfo('PubCommonId decode: Has opted-out');
×
101
      return undefined;
×
102
    }
103
    logInfo(' Decoded value PubCommonId ' + value);
28✔
104
    const idObj = {'pubcid': value};
28✔
105
    return idObj;
28✔
106
  },
107
  /**
108
   * performs action to obtain id
109
   * @function
110
   * @param {SubmoduleConfig} [config] Config object with params and storage properties
111
   * @param {Object} consentData
112
   * @param {string} storedId Existing pubcommon id
113
   * @returns {IdResponse}
114
   */
115
  getId: function (config = {}, consentData, storedId) {
×
116
    if (hasOptedOut()) {
11!
117
      logInfo('PubCommonId: Has opted-out');
×
118
      return;
×
119
    }
120
    if (consentData?.coppa) {
11!
121
      logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId');
×
122
      return;
×
123
    }
124
    const {params: {create = true, pixelUrl} = {}} = config;
11✔
125
    let newId = storedId;
11✔
126
    if (!newId) {
11✔
127
      try {
9✔
128
        if (typeof window[PUB_COMMON_ID] === 'object') {
9!
129
          // If the page includes its own pubcid module, then save a copy of id.
130
          newId = window[PUB_COMMON_ID].getId();
×
131
        }
132
      } catch (e) {
133
      }
134

135
      if (!newId) newId = (create && hasDeviceAccess()) ? generateUUID() : undefined;
9!
136
    }
137

138
    return {id: newId, callback: getIdCallback(newId, pixelUrl)};
11✔
139
  },
140
  /**
141
   * performs action to extend an id.  There are generally two ways to extend the expiration time
142
   * of stored id: using pixelUrl or return the id and let main user id module write it again with
143
   * the new expiration time.
144
   *
145
   * PixelUrl, if defined, should point back to a first party domain endpoint.  On the server
146
   * side, there is either a plugin, or customized logic to read and write back the pubcid cookie.
147
   * The extendId function itself should return only the callback, and not the id itself to avoid
148
   * having the script-side overwriting server-side.  This applies to both pubcid and sharedid.
149
   *
150
   * On the other hand, if there is no pixelUrl, then the extendId should return storedId so that
151
   * its expiration time is updated.
152
   *
153
   * @function
154
   * @param {SubmoduleParams} [config]
155
   * @param {ConsentData|undefined} consentData
156
   * @param {Object} storedId existing id
157
   * @returns {IdResponse|undefined}
158
   */
159
  extendId: function(config = {}, consentData, storedId) {
×
160
    if (hasOptedOut()) {
9!
161
      logInfo('PubCommonId: Has opted-out');
×
162
      return {id: undefined};
×
163
    }
164
    if (consentData?.coppa) {
9!
165
      logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId');
×
166
      return;
×
167
    }
168
    const {params: {extend = false, pixelUrl} = {}} = config;
9✔
169

170
    if (extend) {
9✔
171
      if (pixelUrl) {
1!
172
        const callback = queuePixelCallback(pixelUrl, storedId);
×
173
        return {callback: callback};
×
174
      } else {
175
        return {id: storedId};
1✔
176
      }
177
    }
178
  },
179

180
  domainOverride: domainOverrideToRootDomain(storage, 'sharedId'),
181
  eids: {
182
    'pubcid'(values, config) {
183
      const eid = {
80✔
184
        source: 'pubcid.org',
185
        uids: values.map(id => ({id, atype: 1}))
80✔
186
      }
187
      if (config?.params?.inserter != null) {
80!
188
        eid.inserter = config.params.inserter;
×
189
      }
190
      return eid;
80✔
191
    },
192
  }
193
};
194

195
submodule('userId', sharedIdSystemSubmodule);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc