@adobe/alloy 2.35.0 → 2.35.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/alloy.js CHANGED
@@ -5828,6 +5828,8 @@
5828
5828
  OF ANY KIND, either express or implied. See the License for the specific language
5829
5829
  governing permissions and limitations under the License.
5830
5830
  */
5831
+
5832
+
5831
5833
  const STATE_STORE_HANDLE_TYPE = "state:store";
5832
5834
 
5833
5835
  var createCookieTransfer = ({
@@ -5842,7 +5844,7 @@
5842
5844
  * access first-party cookies, therefore we transfer cookies into
5843
5845
  * the request body so they can be read by the server.
5844
5846
  */
5845
- cookiesToPayload(payload, endpointDomain) {
5847
+ cookiesToPayload(payload, endpointDomain, extraCookieNames = []) {
5846
5848
  // localhost is a special case where the apexDomain is ""
5847
5849
  // We want to treat localhost as a third-party domain.
5848
5850
  const isEndpointFirstParty =
@@ -5855,20 +5857,22 @@
5855
5857
  // If the endpoint is first-party, there's no need to transfer cookies
5856
5858
  // to the payload since they'll be automatically passed through cookie
5857
5859
  // headers.
5858
- if (!isEndpointFirstParty) {
5860
+ if (!isEndpointFirstParty || isNonEmptyArray(extraCookieNames)) {
5859
5861
  const cookies = cookieJar.getAll();
5860
-
5861
- const entries = Object.keys(cookies)
5862
- .filter(shouldTransferCookie)
5863
- .map((qualifyingCookieName) => {
5864
- return {
5865
- key: qualifyingCookieName,
5866
- value: cookies[qualifyingCookieName],
5867
- };
5868
- });
5869
-
5870
- if (entries.length) {
5871
- state.entries = entries;
5862
+ const entriesByKey = new Map();
5863
+ const addEntry = (key) => {
5864
+ if (cookies[key] !== undefined && !entriesByKey.has(key)) {
5865
+ entriesByKey.set(key, { key, value: cookies[key] });
5866
+ }
5867
+ };
5868
+ if (!isEndpointFirstParty) {
5869
+ Object.keys(cookies).filter(shouldTransferCookie).forEach(addEntry);
5870
+ }
5871
+ if (isNonEmptyArray(extraCookieNames)) {
5872
+ extraCookieNames.forEach(addEntry);
5873
+ }
5874
+ if (entriesByKey.size) {
5875
+ state.entries = [...entriesByKey.values()];
5872
5876
  }
5873
5877
  }
5874
5878
 
@@ -5926,6 +5930,9 @@
5926
5930
  const MBOX_EDGE_CLUSTER = "mboxEdgeCluster";
5927
5931
  const AT_QA_MODE = "at_qa_mode";
5928
5932
  const MBOX = "mbox";
5933
+ // The cookie that Marketo (Munchkin) JS sets. It must be forwarded so the
5934
+ // server can resolve the visitor's Marketo identity for Marketo integrations.
5935
+ const MUNCHKIN = "_mkto_trk";
5929
5936
 
5930
5937
  /*
5931
5938
  Copyright 2022 Adobe. All rights reserved.
@@ -5946,10 +5953,13 @@
5946
5953
  // all cookies whose names are namespaced according to the
5947
5954
  // logic in isNamespacedCookieName as well as any legacy
5948
5955
  // cookie names (so that the server can handle migrating
5949
- // identities on websites previously using Visitor.js)
5956
+ // identities on websites previously using Visitor.js). The
5957
+ // Marketo Munchkin cookie is always forwarded so the server
5958
+ // can resolve the visitor's Marketo identity.
5950
5959
  return (
5951
5960
  isNamespacedCookieName(orgId, name) ||
5952
5961
  name === AT_QA_MODE ||
5962
+ name === MUNCHKIN ||
5953
5963
  (targetMigrationEnabled && name === MBOX)
5954
5964
  );
5955
5965
  };
@@ -7746,10 +7756,10 @@
7746
7756
  governing permissions and limitations under the License.
7747
7757
  */
7748
7758
 
7749
- // The 2.35.0 keyword will be replace at alloy build time with the package.json version.
7759
+ // The 2.35.1-beta.1 keyword will be replace at alloy build time with the package.json version.
7750
7760
  // see babel-plugin-version
7751
7761
 
7752
- var libraryVersion = "2.35.0";
7762
+ var libraryVersion = "2.35.1-beta.1";
7753
7763
 
7754
7764
  /*
7755
7765
  Copyright 2019 Adobe. All rights reserved.
@@ -7835,7 +7845,11 @@
7835
7845
  */
7836
7846
 
7837
7847
 
7838
- const coreConfigValidators = createCoreConfigs();
7848
+ // Default, shared for the lifetime of this module — matches "configured
7849
+ // once per page load" in the browser, where module state resets on its own.
7850
+ // Callers that don't map 1:1 to a page load (e.g. one Node process handling
7851
+ // many requests) can pass a fresh instance per call instead.
7852
+ const defaultCoreConfigValidators = createCoreConfigs();
7839
7853
 
7840
7854
  /**
7841
7855
  * @param {Object} params
@@ -7843,12 +7857,14 @@
7843
7857
  * @param {Array<import('./types.js').AlloyMonitor>} [params.monitors]
7844
7858
  * @param {Array<Function>} params.components
7845
7859
  * @param {() => import('../services/index.js').PlatformServices} params.createPlatformServices
7860
+ * @param {ReturnType<typeof createCoreConfigs>} [params.coreConfigValidators]
7846
7861
  */
7847
7862
  const createExecuteCommand = ({
7848
7863
  instanceName,
7849
7864
  monitors = [],
7850
7865
  components,
7851
7866
  createPlatformServices,
7867
+ coreConfigValidators = defaultCoreConfigValidators,
7852
7868
  }) => {
7853
7869
  const platformServices = createPlatformServices();
7854
7870
  const allMonitors = [...platformServices.globals.getMonitors(), ...monitors];
@@ -10210,8 +10226,8 @@
10210
10226
  const ID = "id";
10211
10227
  const IAM_EVENT_TYPE = "iam.eventType";
10212
10228
  const EVENT_TYPE = "eventType";
10213
- const TYPE = "type";
10214
- const VALID_EVENT_TYPES = [IAM_EVENT_TYPE, EVENT_TYPE, TYPE];
10229
+ const TYPE$1 = "type";
10230
+ const VALID_EVENT_TYPES = [IAM_EVENT_TYPE, EVENT_TYPE, TYPE$1];
10215
10231
  const VALID_EVENT_IDS = [IAM_ID, ID];
10216
10232
  const checkForHistoricalMatcher = (eventCount, matcherKey, value) => {
10217
10233
  switch (matcherKey) {
@@ -12892,6 +12908,7 @@
12892
12908
  sendConversationServiceRequest,
12893
12909
  buildEndpointUrl,
12894
12910
  cookieTransfer,
12911
+ alwaysTransferCookies = [],
12895
12912
  createResponse,
12896
12913
  decodeKndctrCookie,
12897
12914
  lifecycle,
@@ -12972,9 +12989,11 @@
12972
12989
  }
12973
12990
 
12974
12991
  payload.addEvent(event);
12975
- if (config.conversation.stickyConversationSession === true) {
12976
- cookieTransfer.cookiesToPayload(payload, edgeDomain);
12977
- }
12992
+ cookieTransfer.cookiesToPayload(
12993
+ payload,
12994
+ edgeDomain,
12995
+ alwaysTransferCookies,
12996
+ );
12978
12997
  return sendConversationServiceRequest({
12979
12998
  requestId: v4(),
12980
12999
  url,
@@ -13145,10 +13164,14 @@
13145
13164
  .default(STREAM_START_TIMEOUT_MS),
13146
13165
  collectSources: boundBoolean().default(false),
13147
13166
  region: boundString().matches(/^[a-z]{2,4}[0-9]{1,2}$/i),
13167
+ // Additional first-party cookie names to always transfer into the
13168
+ // request meta.state object (on top of the ones transferred by default).
13169
+ transferCookies: boundArrayOf(boundString()).default([]),
13148
13170
  }).default({
13149
13171
  stickyConversationSession: false,
13150
13172
  streamTimeout: STREAM_START_TIMEOUT_MS,
13151
13173
  collectSources: false,
13174
+ transferCookies: [],
13152
13175
  }),
13153
13176
  });
13154
13177
 
@@ -13176,6 +13199,8 @@
13176
13199
  cookieTransfer,
13177
13200
  createResponse,
13178
13201
  platformServices,
13202
+ createSendConversationEvent:
13203
+ createSendConversationEventFactory = createSendConversationEvent,
13179
13204
  }) => {
13180
13205
  const session = {
13181
13206
  id: getConciergeSessionCookie({ loggingCookieJar, config }),
@@ -13193,14 +13218,18 @@
13193
13218
  cookieJar: loggingCookieJar,
13194
13219
  logger,
13195
13220
  });
13196
- const sendConversationEvent = createSendConversationEvent({
13221
+ const sendConversationEvent = createSendConversationEventFactory({
13222
+ loggingCookieJar,
13197
13223
  logger,
13198
13224
  eventManager,
13199
13225
  consent,
13226
+ instanceName,
13227
+ sendEdgeNetworkRequest,
13200
13228
  config,
13201
13229
  buildEndpointUrl,
13202
13230
  lifecycle,
13203
13231
  cookieTransfer,
13232
+ alwaysTransferCookies: config.conversation.transferCookies || [],
13204
13233
  createResponse,
13205
13234
  sendConversationServiceRequest,
13206
13235
  decodeKndctrCookie,
@@ -13254,6 +13283,13 @@
13254
13283
  * @param {Array<AlloyMonitor>} [options.monitors] - Monitors for the instance.
13255
13284
  * @param {Array<Function>} [options.components] - Components for the instance.
13256
13285
  * @param {CreatePlatformServices} createPlatformServices
13286
+ * @param {ReturnType<typeof import('./core/config/createCoreConfigs.js').default>} [coreConfigValidators]
13287
+ * Tracks which orgId/datastreamId values have already been configured, to
13288
+ * catch accidentally configuring two instances with the same one. Shared by
13289
+ * default for the module's lifetime (matches "configured once per page
13290
+ * load," since a page load resets module state); pass a fresh instance per
13291
+ * call if that assumption doesn't hold for your runtime (e.g. one Node
13292
+ * process handling many requests).
13257
13293
  * @returns {(commandName: string, options?: Object) => Promise<any>} A callable Alloy instance.
13258
13294
  *
13259
13295
  * @see {@link https://experienceleague.adobe.com/en/docs/experience-platform/web-sdk/install/create-custom-build} for more details.
@@ -13261,6 +13297,7 @@
13261
13297
  const createCustomInstance$1 = (
13262
13298
  options = {},
13263
13299
  createPlatformServices = undefined,
13300
+ coreConfigValidators = undefined,
13264
13301
  ) => {
13265
13302
  const eventOptionsValidator = boundObjectOf({
13266
13303
  name: boundString().default("alloy"),
@@ -13275,6 +13312,7 @@
13275
13312
  monitors,
13276
13313
  components,
13277
13314
  createPlatformServices,
13315
+ coreConfigValidators,
13278
13316
  });
13279
13317
  };
13280
13318
 
@@ -17214,6 +17252,55 @@
17214
17252
  governing permissions and limitations under the License.
17215
17253
  */
17216
17254
 
17255
+ const TYPE = "type";
17256
+ const MODULE = "module";
17257
+ const NONCE = "nonce";
17258
+ const NOMODULE = "nomodule";
17259
+
17260
+ // Per the HTML spec, a <script> only ever executes if its type is absent/
17261
+ // empty, "module", or one of these recognized JavaScript MIME type essences
17262
+ // (https://mimesniff.spec.whatwg.org/#javascript-mime-type). Any other type
17263
+ // (e.g. "importmap", "application/json", a templating library's custom type)
17264
+ // makes the element a data block: the browser never executes it, regardless
17265
+ // of whether it's parsed from HTML, inserted via innerHTML, or appended with
17266
+ // appendChild. Such elements are left untouched by this module — extracting
17267
+ // and re-inserting them would be pointless at best, and at worst (if `type`
17268
+ // were dropped along the way) would turn inert data into code that runs.
17269
+ const JAVASCRIPT_MIME_TYPES = new Set([
17270
+ "application/ecmascript",
17271
+ "application/javascript",
17272
+ "application/x-ecmascript",
17273
+ "application/x-javascript",
17274
+ "text/ecmascript",
17275
+ "text/javascript",
17276
+ "text/javascript1.0",
17277
+ "text/javascript1.1",
17278
+ "text/javascript1.2",
17279
+ "text/javascript1.3",
17280
+ "text/javascript1.4",
17281
+ "text/javascript1.5",
17282
+ "text/jscript",
17283
+ "text/livescript",
17284
+ "text/x-ecmascript",
17285
+ "text/x-javascript",
17286
+ ]);
17287
+
17288
+ // Attributes that affect how the script executes/fetches (as opposed to
17289
+ // identity/presentation attributes like id, class, and data-*, which are
17290
+ // intentionally not copied — see loadScript below). `src` is handled
17291
+ // separately since it needs different treatment for inline vs. remote
17292
+ // scripts, and `nonce` is copied here as a fallback but then overridden
17293
+ // below with the page's current CSP nonce when one is found.
17294
+ const COPIED_ATTRIBUTES = [
17295
+ TYPE,
17296
+ NONCE,
17297
+ "crossorigin",
17298
+ "integrity",
17299
+ "referrerpolicy",
17300
+ "fetchpriority",
17301
+ "nomodule",
17302
+ ];
17303
+
17217
17304
  const getPromise = (url, script) => {
17218
17305
  return new Promise((resolve, reject) => {
17219
17306
  script.onload = () => {
@@ -17224,24 +17311,45 @@
17224
17311
  };
17225
17312
  });
17226
17313
  };
17314
+
17315
+ // Re-creates a script in <head> so the browser executes it (scripts inserted via
17316
+ // innerHTML never run). Handles both remote scripts (with a src) and inline
17317
+ // module scripts: an inline `type="module"` executes asynchronously and belongs
17318
+ // with the head-loaded scripts rather than the synchronously-run inline path.
17227
17319
  const loadScript = (source) => {
17228
17320
  const url = getAttribute(source, SRC);
17229
17321
  const script = document.createElement("script");
17230
- // Preserve all author-supplied attributes (class, type, data-*, etc.) so
17231
- // consumers relying on them keep working.
17232
- const { attributes } = source;
17233
- for (let i = 0; i < attributes.length; i += 1) {
17234
- const { name, value } = attributes[i];
17235
- script.setAttribute(name, value);
17236
- }
17237
- // Override invariants last so they can't be clobbered by the source element.
17322
+ // Only carry the attributes required to execute the script correctly:
17323
+ // `src` (below) and COPIED_ATTRIBUTES. Author attributes such as `id`,
17324
+ // `class`, and `data-*` are intentionally NOT copied: the original offer
17325
+ // <script> is left in the page (inert), so copying its id/class onto this
17326
+ // executed element would create duplicate matches for
17327
+ // document.getElementById / querySelector.
17328
+ COPIED_ATTRIBUTES.forEach((name) => {
17329
+ const value = getAttribute(source, name);
17330
+ if (value !== null) {
17331
+ script.setAttribute(name, value);
17332
+ }
17333
+ });
17334
+ // The page's current CSP nonce takes priority over whatever nonce (if any)
17335
+ // was written into the source markup, since that's the one the browser
17336
+ // will actually check against the CSP header for this page load.
17238
17337
  const nonce = getNonce();
17239
17338
  if (nonce) {
17240
- script.setAttribute("nonce", nonce);
17339
+ script.setAttribute(NONCE, nonce);
17241
17340
  }
17341
+
17342
+ if (!url) {
17343
+ // Inline module script: carry the code across and let it execute in <head>.
17344
+ // Inline scripts emit no load event, so completion cannot be tracked; we
17345
+ // resolve immediately and rely on the browser's deferred module execution.
17346
+ script.textContent = source.textContent;
17347
+ document.head.appendChild(script);
17348
+ return Promise.resolve(script);
17349
+ }
17350
+
17351
+ script.src = url;
17242
17352
  script.async = true;
17243
- // Assign load handlers after copying attributes so a copied inline
17244
- // onload/onerror attribute can't break load-completion tracking.
17245
17353
  const promise = getPromise(url, script);
17246
17354
  document.head.appendChild(script);
17247
17355
  return promise;
@@ -17250,11 +17358,59 @@
17250
17358
  const is = (element, tagName) =>
17251
17359
  !!element && element.tagName === tagName;
17252
17360
 
17253
- const isInlineScript = (element) =>
17254
- is(element, SCRIPT) && !getAttribute(element, SRC);
17361
+ // Normalizes away casing/whitespace/parameters (e.g. "TEXT/JAVASCRIPT ;
17362
+ // charset=utf-8") so it can be compared against MODULE/JAVASCRIPT_MIME_TYPES.
17363
+ const getNormalizedType = (element) => {
17364
+ const type = getAttribute(element, TYPE);
17365
+ return type ? type.split(";")[0].trim().toLowerCase() : "";
17366
+ };
17367
+
17368
+ const isModule = (element) => getNormalizedType(element) === MODULE;
17369
+
17370
+ // An empty src (`<script src="">`) is treated as "no src" — it points nowhere,
17371
+ // so the script is handled on the inline path rather than fetched.
17372
+ const hasSrc = (element) => !!getAttribute(element, SRC);
17255
17373
 
17256
- const isRemoteScript = (element) =>
17257
- is(element, SCRIPT) && getAttribute(element, SRC);
17374
+ // A <script> the browser would actually execute: a classic script (type is
17375
+ // absent/empty or a recognized JavaScript MIME type) or an ES module. Any other
17376
+ // type is a data block (e.g. "importmap", "application/json", a templating
17377
+ // library's custom type) that never runs — see JAVASCRIPT_MIME_TYPES above.
17378
+ // Only executable scripts are extracted and re-inserted; the rest are left
17379
+ // untouched, since forcing them to run was never the intent.
17380
+ const isExecutableScript = (element) => {
17381
+ if (!is(element, SCRIPT)) {
17382
+ return false;
17383
+ }
17384
+ const type = getNormalizedType(element);
17385
+ return type === "" || type === MODULE || JAVASCRIPT_MIME_TYPES.has(type);
17386
+ };
17387
+
17388
+ // How a given <script> must be re-created for the browser to run it:
17389
+ // HEAD_SCRIPT – re-created in <head>: any remote script (with a src), plus
17390
+ // inline ES modules. An inline `type="module"` executes
17391
+ // asynchronously, so it belongs with the head scripts rather
17392
+ // than the synchronous inline path.
17393
+ // INLINE_SCRIPT – classic inline script: re-created and run synchronously in
17394
+ // the offer container.
17395
+ const HEAD_SCRIPT = "head";
17396
+ const INLINE_SCRIPT = "inline";
17397
+
17398
+ // Classifies a <script>, or returns null when there's nothing to run: either a
17399
+ // data block the browser would never execute (left untouched), or an inline
17400
+ // script with no code. `src`-less inline scripts need code to be worth running;
17401
+ // a remote script's code lives at its `src`, so it always qualifies.
17402
+ const classifyScript = (element) => {
17403
+ if (!isExecutableScript(element)) {
17404
+ return null;
17405
+ }
17406
+ if (hasSrc(element)) {
17407
+ return HEAD_SCRIPT;
17408
+ }
17409
+ if (!element.textContent) {
17410
+ return null;
17411
+ }
17412
+ return isModule(element) ? HEAD_SCRIPT : INLINE_SCRIPT;
17413
+ };
17258
17414
 
17259
17415
  const getInlineScripts = (fragment) => {
17260
17416
  const scripts = selectNodes(SCRIPT, fragment);
@@ -17268,17 +17424,24 @@
17268
17424
  for (let i = 0; i < length; i += 1) {
17269
17425
  const element = scripts[i];
17270
17426
 
17271
- if (!isInlineScript(element)) {
17427
+ if (classifyScript(element) !== INLINE_SCRIPT) {
17272
17428
  continue;
17273
17429
  }
17274
17430
 
17275
17431
  const { textContent } = element;
17276
17432
 
17277
- if (!textContent) {
17278
- continue;
17279
- }
17280
-
17281
- result.push(createNode(SCRIPT, attributes, { textContent }));
17433
+ // `nomodule` is preserved even though this element is force-executed via
17434
+ // appendChild: without it, a classic script the author intended as a
17435
+ // legacy fallback (skipped in module-supporting browsers) would run
17436
+ // where it otherwise wouldn't have.
17437
+ const nomodule = getAttribute(element, NOMODULE);
17438
+ result.push(
17439
+ createNode(
17440
+ SCRIPT,
17441
+ { ...attributes, ...(nomodule !== null && { nomodule }) },
17442
+ { textContent },
17443
+ ),
17444
+ );
17282
17445
  }
17283
17446
 
17284
17447
  return result;
@@ -17292,8 +17455,7 @@
17292
17455
  for (let i = 0; i < length; i += 1) {
17293
17456
  const element = scripts[i];
17294
17457
 
17295
- // isRemoteScript already requires a non-empty src attribute.
17296
- if (!isRemoteScript(element)) {
17458
+ if (classifyScript(element) !== HEAD_SCRIPT) {
17297
17459
  continue;
17298
17460
  }
17299
17461