@adobe/alloy 2.36.0-beta.1 → 2.36.1-beta.0

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
@@ -3750,7 +3750,14 @@
3750
3750
  /**
3751
3751
  * Send a network request and returns details about the response.
3752
3752
  */
3753
- return ({ requestId, url, payload, useSendBeacon, headers }) => {
3753
+ return ({
3754
+ requestId,
3755
+ url,
3756
+ payload,
3757
+ useSendBeacon,
3758
+ useKeepalive,
3759
+ headers,
3760
+ }) => {
3754
3761
  // We want to log raw payload and event data rather than
3755
3762
  // our fancy wrapper objects. Calling payload.toJSON() is
3756
3763
  // insufficient to get all the nested raw data, because it's
@@ -3771,53 +3778,55 @@
3771
3778
  // navigator.sendBeacon() has no way to attach custom headers, so a
3772
3779
  // request that needs any (e.g. authenticated Server API requests)
3773
3780
  // must always go through fetch, regardless of useSendBeacon.
3774
- const requestMethod =
3775
- useSendBeacon && !headers ? sendBeaconRequest : sendFetchRequest;
3781
+ const request =
3782
+ useSendBeacon && !headers
3783
+ ? sendBeaconRequest(url, stringifiedPayload)
3784
+ : sendFetchRequest(url, stringifiedPayload, headers, {
3785
+ keepalive: Boolean(useKeepalive),
3786
+ });
3776
3787
 
3777
- return requestMethod(url, stringifiedPayload, headers).then(
3778
- (response) => {
3779
- const requestIsRetryable = isRequestRetryable({
3788
+ return request.then((response) => {
3789
+ const requestIsRetryable = isRequestRetryable({
3790
+ response,
3791
+ retriesAttempted,
3792
+ });
3793
+
3794
+ if (requestIsRetryable) {
3795
+ const requestRetryDelay = getRequestRetryDelay({
3780
3796
  response,
3781
3797
  retriesAttempted,
3782
3798
  });
3799
+ return new Promise((resolve) => {
3800
+ setTimeout(() => {
3801
+ resolve(executeRequest(retriesAttempted + 1));
3802
+ }, requestRetryDelay);
3803
+ });
3804
+ }
3783
3805
 
3784
- if (requestIsRetryable) {
3785
- const requestRetryDelay = getRequestRetryDelay({
3786
- response,
3787
- retriesAttempted,
3788
- });
3789
- return new Promise((resolve) => {
3790
- setTimeout(() => {
3791
- resolve(executeRequest(retriesAttempted + 1));
3792
- }, requestRetryDelay);
3793
- });
3794
- }
3795
-
3796
- let parsedBody;
3806
+ let parsedBody;
3797
3807
 
3798
- try {
3799
- parsedBody = JSON.parse(response.body);
3800
- } catch {
3801
- // Non-JSON. Something went wrong.
3802
- }
3808
+ try {
3809
+ parsedBody = JSON.parse(response.body);
3810
+ } catch {
3811
+ // Non-JSON. Something went wrong.
3812
+ }
3803
3813
 
3804
- logger.logOnNetworkResponse({
3805
- requestId,
3806
- url,
3807
- payload: parsedPayload,
3808
- ...response,
3809
- parsedBody,
3810
- retriesAttempted,
3811
- });
3814
+ logger.logOnNetworkResponse({
3815
+ requestId,
3816
+ url,
3817
+ payload: parsedPayload,
3818
+ ...response,
3819
+ parsedBody,
3820
+ retriesAttempted,
3821
+ });
3812
3822
 
3813
- return {
3814
- statusCode: response.statusCode,
3815
- body: response.body,
3816
- parsedBody,
3817
- getHeader: response.getHeader,
3818
- };
3819
- },
3820
- );
3823
+ return {
3824
+ statusCode: response.statusCode,
3825
+ body: response.body,
3826
+ parsedBody,
3827
+ getHeader: response.getHeader,
3828
+ };
3829
+ });
3821
3830
  };
3822
3831
 
3823
3832
  return executeRequest().catch((error) => {
@@ -5229,6 +5238,15 @@
5229
5238
  },
5230
5239
  logOnNetworkResponse(data) {
5231
5240
  notifyMonitors("onNetworkResponse", data);
5241
+ if (data.sentWithBeacon) {
5242
+ // sendBeacon only tells us the browser queued the request; there is
5243
+ // no server response to report.
5244
+ log(
5245
+ "info",
5246
+ `Request ${data.requestId}: Sent using sendBeacon. No response is available for sendBeacon requests. If the page navigates away, the browser keeps sending the request in the background, but its developer tools may show it as canceled.`,
5247
+ );
5248
+ return;
5249
+ }
5232
5250
  const messagesSuffix =
5233
5251
  data.parsedBody || data.body ? `response body:` : `no response body.`;
5234
5252
  log(
@@ -5335,6 +5353,9 @@
5335
5353
  * @param {RequestPayload} options.payload
5336
5354
  * @param {function({isIdentityEstablished: boolean}): string} options.getAction
5337
5355
  * @param {function({isIdentityEstablished: boolean}): boolean} options.getUseSendBeacon
5356
+ * @param {function(): boolean} [options.getUseKeepalive] Whether a fetch
5357
+ * request should be sent with `keepalive` so it survives a page unload.
5358
+ * Defaults to false.
5338
5359
  * @param {string} [options.datastreamIdOverride]
5339
5360
  * @param {string} [options.edgeSubPath]
5340
5361
  *
@@ -5358,6 +5379,7 @@
5358
5379
  payload,
5359
5380
  getAction,
5360
5381
  getUseSendBeacon,
5382
+ getUseKeepalive = () => false,
5361
5383
  datastreamIdOverride,
5362
5384
  edgeSubPath,
5363
5385
  requestParams = {},
@@ -5383,6 +5405,9 @@
5383
5405
  getUseSendBeacon() {
5384
5406
  return getUseSendBeacon({ isIdentityEstablished });
5385
5407
  },
5408
+ getUseKeepalive() {
5409
+ return getUseKeepalive();
5410
+ },
5386
5411
  getEdgeSubPath() {
5387
5412
  if (edgeSubPath) {
5388
5413
  return edgeSubPath;
@@ -5451,9 +5476,9 @@
5451
5476
  // No error will be thrown and an identity is established. This is good.
5452
5477
  // 3. The document is unloaded and Alloy does not receive the empty response
5453
5478
  // back from the collect endpoint before navigation is completed. In this
5454
- // case, no error is thrown, but no identity was established and it's
5455
- // more likely that the request never makes it to the server because we're
5456
- // using fetch instead of sendBeacon.
5479
+ // case, no error is thrown, but no identity was established. Because the
5480
+ // fetch is sent with `keepalive` (see getUseKeepalive below), the browser
5481
+ // still delivers the request to the server after the page unloads.
5457
5482
  //
5458
5483
  // The second approach seemed preferable.
5459
5484
  return (
@@ -5462,6 +5487,12 @@
5462
5487
  );
5463
5488
  };
5464
5489
 
5490
+ // When the document may be unloading but we use fetch anyway (for example,
5491
+ // before an identity is established), send the fetch with `keepalive` so the
5492
+ // browser doesn't cancel the request when the page navigates away.
5493
+ const getUseKeepalive = () =>
5494
+ dataCollectionRequestPayload.getDocumentMayUnload();
5495
+
5465
5496
  return createRequest({
5466
5497
  payload: dataCollectionRequestPayload,
5467
5498
  getAction({ isIdentityEstablished }) {
@@ -5470,6 +5501,7 @@
5470
5501
  : "interact";
5471
5502
  },
5472
5503
  getUseSendBeacon,
5504
+ getUseKeepalive,
5473
5505
  datastreamIdOverride,
5474
5506
  });
5475
5507
  };
@@ -6335,6 +6367,7 @@
6335
6367
  url,
6336
6368
  payload: outgoingPayload,
6337
6369
  useSendBeacon: request.getUseSendBeacon(),
6370
+ useKeepalive: request.getUseKeepalive(),
6338
6371
  headers,
6339
6372
  });
6340
6373
  })
@@ -6355,6 +6388,7 @@
6355
6388
  url,
6356
6389
  payload,
6357
6390
  useSendBeacon: request.getUseSendBeacon(),
6391
+ useKeepalive: request.getUseKeepalive(),
6358
6392
  });
6359
6393
  }
6360
6394
  return handleRequestFailure(onRequestFailureCallbackAggregator)(error);
@@ -7956,10 +7990,10 @@
7956
7990
  governing permissions and limitations under the License.
7957
7991
  */
7958
7992
 
7959
- // The 2.36.0-beta.1 keyword will be replace at alloy build time with the package.json version.
7993
+ // The 2.36.1-beta.0 keyword will be replace at alloy build time with the package.json version.
7960
7994
  // see babel-plugin-version
7961
7995
 
7962
- var libraryVersion = "2.36.0-beta.1";
7996
+ var libraryVersion = "2.36.1-beta.0";
7963
7997
 
7964
7998
  /*
7965
7999
  Copyright 2019 Adobe. All rights reserved.
@@ -22117,9 +22151,23 @@
22117
22151
  governing permissions and limitations under the License.
22118
22152
  */
22119
22153
 
22120
- var injectSendFetchRequest = ({ fetch }) => {
22121
- return (url, body, headers) => {
22122
- return fetch(url, {
22154
+ /** @import { NetworkService } from "@adobe/alloy-core/services" */
22155
+
22156
+ /**
22157
+ * @param {Object} params
22158
+ * @param {typeof window.fetch} params.fetch
22159
+ * @param {{ info: (...args: any[]) => void }} params.logger
22160
+ * @returns {NetworkService["sendFetchRequest"]}
22161
+ */
22162
+ var injectSendFetchRequest = ({ fetch, logger }) => {
22163
+ /**
22164
+ * @param {string} url
22165
+ * @param {string} body
22166
+ * @param {Record<string, string> | undefined} headers
22167
+ * @param {boolean} keepalive
22168
+ */
22169
+ const startFetch = (url, body, headers, keepalive) =>
22170
+ fetch(url, {
22123
22171
  method: "POST",
22124
22172
  cache: "no-cache",
22125
22173
  credentials: "include", // To set the cookie header in the request.
@@ -22129,7 +22177,30 @@
22129
22177
  },
22130
22178
  referrerPolicy: "no-referrer-when-downgrade",
22131
22179
  body,
22132
- }).then((response) => {
22180
+ ...(keepalive ? { keepalive: true } : {}),
22181
+ });
22182
+
22183
+ return (url, body, headers, { keepalive = false } = {}) => {
22184
+ let responsePromise = startFetch(url, body, headers, keepalive);
22185
+
22186
+ if (keepalive) {
22187
+ // Browsers reject keepalive requests whose body, together with every
22188
+ // other in-flight keepalive request and sendBeacon on the page, exceeds
22189
+ // 64 KiB. The rejection is a TypeError that looks the same as any other
22190
+ // network error ("Failed to fetch", "NetworkError when attempting to
22191
+ // fetch resource.", "Load failed") and arrives before anything is sent,
22192
+ // so retry once without keepalive. Only fetch() itself is covered here;
22193
+ // a failure while reading the response is not retried because the
22194
+ // server already received the request.
22195
+ responsePromise = responsePromise.catch(() => {
22196
+ logger.info(
22197
+ "Unable to send the request with `keepalive`; falling back to a regular `fetch`.",
22198
+ );
22199
+ return startFetch(url, body, headers, false);
22200
+ });
22201
+ }
22202
+
22203
+ return responsePromise.then((response) => {
22133
22204
  return response.text().then((responseBody) => ({
22134
22205
  statusCode: response.status,
22135
22206
  // We expose headers through a function instead of creating an object
@@ -22166,13 +22237,15 @@
22166
22237
 
22167
22238
  // Using sendBeacon, we technically don't get a response back from
22168
22239
  // the server, but we'll resolve the promise with an object to maintain
22169
- // consistency with other network strategies.
22240
+ // consistency with other network strategies. `sentWithBeacon` marks the
22241
+ // response as synthetic so it isn't logged as a real server response.
22170
22242
  return Promise.resolve({
22171
22243
  statusCode: 204,
22172
22244
  getHeader() {
22173
22245
  return null;
22174
22246
  },
22175
22247
  body: "",
22248
+ sentWithBeacon: true,
22176
22249
  });
22177
22250
  };
22178
22251
  };
@@ -22196,7 +22269,7 @@
22196
22269
  */
22197
22270
  const createBrowserNetworkService = ({ logger }) => {
22198
22271
  const { fetch, navigator } = window;
22199
- const sendFetchRequest = injectSendFetchRequest({ fetch });
22272
+ const sendFetchRequest = injectSendFetchRequest({ fetch, logger });
22200
22273
  const sendBeaconRequest =
22201
22274
  typeof navigator.sendBeacon === "function"
22202
22275
  ? injectSendBeaconRequest({