@pack/hydrogen 3.1.1-beta.0 → 3.1.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/index.js CHANGED
@@ -4356,7 +4356,7 @@ var require_dist = __commonJS({
4356
4356
  }
4357
4357
  });
4358
4358
 
4359
- // node_modules/@pack/packlytics/dist/utils/get-packlytics-id.js
4359
+ // ../packlytics/dist/utils/get-packlytics-id.js
4360
4360
  function sha256(ascii) {
4361
4361
  function rightRotate(value, amount) {
4362
4362
  return value >>> amount | value << 32 - amount;
@@ -4471,7 +4471,7 @@ var parseAcceptLanguage = function parseAcceptLanguage2(languageHeaderValue, opt
4471
4471
  });
4472
4472
  };
4473
4473
 
4474
- // node_modules/@pack/packlytics/dist/utils/get-headers.js
4474
+ // ../packlytics/dist/utils/get-headers.js
4475
4475
  function getHeaders(requestOrHeaders) {
4476
4476
  if (requestOrHeaders instanceof Request) {
4477
4477
  return requestOrHeaders.headers;
@@ -4479,7 +4479,7 @@ function getHeaders(requestOrHeaders) {
4479
4479
  return requestOrHeaders;
4480
4480
  }
4481
4481
 
4482
- // node_modules/@pack/packlytics/dist/utils/get-client-locales.js
4482
+ // ../packlytics/dist/utils/get-client-locales.js
4483
4483
  function getClientLocales(requestOrHeaders) {
4484
4484
  let headers = getHeaders(requestOrHeaders);
4485
4485
  let acceptLanguage = headers.get("Accept-Language");
@@ -4494,7 +4494,7 @@ function getClientLocales(requestOrHeaders) {
4494
4494
  return locales;
4495
4495
  }
4496
4496
 
4497
- // node_modules/@pack/packlytics/dist/utils/sanatize-payload.js
4497
+ // ../packlytics/dist/utils/sanatize-payload.js
4498
4498
  var sanitizePayload = (data) => {
4499
4499
  let dataStr = JSON.stringify(data);
4500
4500
  const sensitiveFields = [
@@ -4525,7 +4525,7 @@ var sanitizePayload = (data) => {
4525
4525
  return dataStr;
4526
4526
  };
4527
4527
 
4528
- // node_modules/@pack/packlytics/dist/utils/send-event.js
4528
+ // ../packlytics/dist/utils/send-event.js
4529
4529
  var metadata = null;
4530
4530
  var getPacklyticsMetadata = async () => {
4531
4531
  if (metadata)
@@ -4583,14 +4583,14 @@ var sendEvent = (storefrontId, sessionId) => {
4583
4583
  };
4584
4584
  };
4585
4585
 
4586
- // node_modules/@pack/packlytics/dist/utils/page-hit.js
4586
+ // ../packlytics/dist/utils/page-hit.js
4587
4587
  var trackPageHit = (storefrontId, sessionId) => {
4588
4588
  return (eventPayload = {}) => {
4589
4589
  return sendEvent(storefrontId, sessionId)("page_hit", eventPayload);
4590
4590
  };
4591
4591
  };
4592
4592
 
4593
- // node_modules/@pack/packlytics/dist/utils/get-client-device.js
4593
+ // ../packlytics/dist/utils/get-client-device.js
4594
4594
  var import_ua_parser_js = __toESM(require_ua_parser());
4595
4595
  function getDevice(userAgent) {
4596
4596
  const userAgentData = (0, import_ua_parser_js.default)(userAgent);
@@ -4608,16 +4608,16 @@ function getDevice(userAgent) {
4608
4608
  };
4609
4609
  }
4610
4610
 
4611
- // node_modules/@pack/packlytics/dist/packlytics.js
4611
+ // ../packlytics/dist/packlytics.js
4612
4612
  import { getStorefrontHeaders } from "@shopify/hydrogen/oxygen";
4613
4613
 
4614
- // node_modules/@pack/packlytics/dist/utils/get-client-location.js
4614
+ // ../packlytics/dist/utils/get-client-location.js
4615
4615
  function getClientLocation(locale) {
4616
4616
  const localeSplit = locale.split("-");
4617
4617
  return localeSplit.length > 1 ? localeSplit[1].toLowerCase() : "";
4618
4618
  }
4619
4619
 
4620
- // node_modules/@pack/packlytics/dist/packlytics.js
4620
+ // ../packlytics/dist/packlytics.js
4621
4621
  async function sendEvent2(request, session, params) {
4622
4622
  if (process.env.NODE_ENV === "development") {
4623
4623
  return;
@@ -4691,7 +4691,6 @@ var PACK_COOKIE_ID = "__pack";
4691
4691
  var PACK_USER_CONSENT_COOKIE_ID = "__pack_user_consent";
4692
4692
  var PACK_TEST_COOKIE_ID = "__pack_test";
4693
4693
  var PACK_COOKIE_MAX_AGE = 60 * 60 * 24 * 360;
4694
- var PACK_TEST_CACHE_CHECK_URL = "https://test-cache-check-production.packdigital.workers.dev/tests-updated-at";
4695
4694
 
4696
4695
  // src/session/usePackCookies.ts
4697
4696
  function usePackCookies(options) {
@@ -5028,6 +5027,10 @@ var cookie2 = __toESM(require_cookie(), 1);
5028
5027
  var import_json_rules_engine = __toESM(require_dist(), 1);
5029
5028
  var import_debug2 = __toESM(require_browser(), 1);
5030
5029
  var debug2 = (0, import_debug2.default)("pack:ab-testing:local-resolver");
5030
+ function getImpressionSectionIds(testVariant) {
5031
+ const sectionIds = testVariant?.sectionTestVariants?.map((variant) => variant?.section?.id).filter((id) => Boolean(id)) || [];
5032
+ return [...new Set(sectionIds)];
5033
+ }
5031
5034
  var LocalTestResolver = class {
5032
5035
  testRules = [];
5033
5036
  constructor() {
@@ -5066,62 +5069,86 @@ var LocalTestResolver = class {
5066
5069
  * Evaluate test rules and assign a variant
5067
5070
  */
5068
5071
  async assignTest(attributes, _sessionId) {
5069
- debug2("[Pack Test LocalResolver] Starting test assignment:", JSON.stringify({
5070
- totalRules: this.testRules.length,
5071
- attributes
5072
- }));
5072
+ debug2(
5073
+ "[Pack Test LocalResolver] Starting test assignment:",
5074
+ JSON.stringify({
5075
+ totalRules: this.testRules.length,
5076
+ attributes
5077
+ })
5078
+ );
5073
5079
  if (!this.testRules.length) {
5074
5080
  debug2("[Pack Test LocalResolver] No test rules available");
5075
5081
  return null;
5076
5082
  }
5077
5083
  const eligibleTests = await this.evaluateRules(this.testRules, attributes);
5078
- debug2("[Pack Test LocalResolver] Rule evaluation result:", JSON.stringify({
5079
- totalTests: this.testRules.length,
5080
- eligibleTests: eligibleTests.length,
5081
- eligibleTestIds: eligibleTests.map((t) => ({ id: t.id, handle: t.handle }))
5082
- }));
5084
+ debug2(
5085
+ "[Pack Test LocalResolver] Rule evaluation result:",
5086
+ JSON.stringify({
5087
+ totalTests: this.testRules.length,
5088
+ eligibleTests: eligibleTests.length,
5089
+ eligibleTestIds: eligibleTests.map((t) => ({
5090
+ id: t.id,
5091
+ handle: t.handle
5092
+ }))
5093
+ })
5094
+ );
5083
5095
  if (!eligibleTests.length) {
5084
5096
  debug2("[Pack Test LocalResolver] No eligible tests found");
5085
5097
  return null;
5086
5098
  }
5087
5099
  const randomTestIndex = this.getRandomNumber(eligibleTests.length - 1);
5088
5100
  const selectedTest = eligibleTests[randomTestIndex];
5089
- debug2("[Pack Test LocalResolver] Selected test:", JSON.stringify({
5090
- testId: selectedTest.id,
5091
- testHandle: selectedTest.handle,
5092
- randomIndex: randomTestIndex,
5093
- totalEligible: eligibleTests.length
5094
- }));
5101
+ debug2(
5102
+ "[Pack Test LocalResolver] Selected test:",
5103
+ JSON.stringify({
5104
+ testId: selectedTest.id,
5105
+ testHandle: selectedTest.handle,
5106
+ randomIndex: randomTestIndex,
5107
+ totalEligible: eligibleTests.length
5108
+ })
5109
+ );
5095
5110
  const randomPercentage = this.getRandomNumber(100, 1);
5096
5111
  let accumulatedPercentage = 0;
5097
- debug2("[Pack Test LocalResolver] Starting variant assignment:", JSON.stringify({
5098
- randomPercentage,
5099
- variants: selectedTest.testVariants.map((v) => ({
5100
- id: v.id,
5101
- handle: v.handle,
5102
- trafficPercentage: v.trafficPercentage
5103
- }))
5104
- }));
5112
+ debug2(
5113
+ "[Pack Test LocalResolver] Starting variant assignment:",
5114
+ JSON.stringify({
5115
+ randomPercentage,
5116
+ variants: selectedTest.testVariants.map((v) => ({
5117
+ id: v.id,
5118
+ handle: v.handle,
5119
+ trafficPercentage: v.trafficPercentage
5120
+ }))
5121
+ })
5122
+ );
5105
5123
  for (const variant of selectedTest.testVariants) {
5106
5124
  accumulatedPercentage += variant.trafficPercentage * 100;
5107
- debug2("[Pack Test LocalResolver] Checking variant:", JSON.stringify({
5108
- variantId: variant.id,
5109
- variantHandle: variant.handle,
5110
- trafficPercentage: variant.trafficPercentage,
5111
- accumulatedPercentage,
5112
- randomPercentage,
5113
- isSelected: accumulatedPercentage >= randomPercentage
5114
- }));
5125
+ debug2(
5126
+ "[Pack Test LocalResolver] Checking variant:",
5127
+ JSON.stringify({
5128
+ variantId: variant.id,
5129
+ variantHandle: variant.handle,
5130
+ trafficPercentage: variant.trafficPercentage,
5131
+ accumulatedPercentage,
5132
+ randomPercentage,
5133
+ isSelected: accumulatedPercentage >= randomPercentage
5134
+ })
5135
+ );
5115
5136
  if (accumulatedPercentage >= randomPercentage) {
5137
+ const impressionSectionIds = getImpressionSectionIds(variant);
5116
5138
  const result = {
5117
5139
  id: selectedTest.id,
5118
5140
  handle: selectedTest.handle,
5141
+ impressionTrigger: selectedTest.impressionTrigger,
5119
5142
  testVariant: {
5120
5143
  id: variant.id,
5121
5144
  handle: variant.handle
5122
- }
5145
+ },
5146
+ impression: impressionSectionIds.length > 0 ? { sectionIds: impressionSectionIds } : void 0
5123
5147
  };
5124
- debug2("[Pack Test LocalResolver] Variant selected:", JSON.stringify(result));
5148
+ debug2(
5149
+ "[Pack Test LocalResolver] Variant selected:",
5150
+ JSON.stringify(result)
5151
+ );
5125
5152
  return result;
5126
5153
  }
5127
5154
  }
@@ -5132,26 +5159,34 @@ var LocalTestResolver = class {
5132
5159
  * Evaluate rules using json-rules-engine (same as tests-service)
5133
5160
  */
5134
5161
  async evaluateRules(tests, attributes) {
5135
- debug2("[Pack Test LocalResolver] Starting rule evaluation:", JSON.stringify({
5136
- totalTests: tests.length,
5137
- attributes,
5138
- isClientSide: typeof window !== "undefined"
5139
- }));
5162
+ debug2(
5163
+ "[Pack Test LocalResolver] Starting rule evaluation:",
5164
+ JSON.stringify({
5165
+ totalTests: tests.length,
5166
+ attributes,
5167
+ isClientSide: typeof window !== "undefined"
5168
+ })
5169
+ );
5140
5170
  if (typeof window !== "undefined") {
5141
5171
  debug2("[Pack Test LocalResolver] Client side - returning all tests");
5142
5172
  return tests;
5143
5173
  }
5144
5174
  const engineResultsPromises = tests.map((test, index) => {
5145
5175
  const { rules } = test;
5146
- debug2(`[Pack Test LocalResolver] Processing test ${index}:`, JSON.stringify({
5147
- testId: test.id,
5148
- testHandle: test.handle,
5149
- rulesCount: rules?.length || 0,
5150
- rules
5151
- }));
5176
+ debug2(
5177
+ `[Pack Test LocalResolver] Processing test ${index}:`,
5178
+ JSON.stringify({
5179
+ testId: test.id,
5180
+ testHandle: test.handle,
5181
+ rulesCount: rules?.length || 0,
5182
+ rules
5183
+ })
5184
+ );
5152
5185
  if (rules && Array.isArray(rules)) {
5153
5186
  if (rules.length === 0) {
5154
- debug2(`[Pack Test LocalResolver] Test ${test.handle} has no rules - targeting all`);
5187
+ debug2(
5188
+ `[Pack Test LocalResolver] Test ${test.handle} has no rules - targeting all`
5189
+ );
5155
5190
  return Promise.resolve({
5156
5191
  events: [{ type: "targeted" }]
5157
5192
  });
@@ -5163,7 +5198,10 @@ var LocalTestResolver = class {
5163
5198
  operator: rule.operator,
5164
5199
  value: rule.value
5165
5200
  }));
5166
- debug2(`[Pack Test LocalResolver] Test ${test.handle} engine conditions:`, JSON.stringify(engineConditionalProperties));
5201
+ debug2(
5202
+ `[Pack Test LocalResolver] Test ${test.handle} engine conditions:`,
5203
+ JSON.stringify(engineConditionalProperties)
5204
+ );
5167
5205
  const engineRules = [
5168
5206
  new import_json_rules_engine.Rule({
5169
5207
  conditions: {
@@ -5210,29 +5248,46 @@ var LocalTestResolver = class {
5210
5248
  (factValue) => !factValue
5211
5249
  );
5212
5250
  const result = engine.run(attributes);
5213
- debug2(`[Pack Test LocalResolver] Engine result for test ${test.handle}:`, JSON.stringify(result));
5251
+ debug2(
5252
+ `[Pack Test LocalResolver] Engine result for test ${test.handle}:`,
5253
+ JSON.stringify(result)
5254
+ );
5214
5255
  return result;
5215
5256
  }
5216
5257
  }
5217
- debug2(`[Pack Test LocalResolver] Test ${test.handle} has invalid rules`);
5258
+ debug2(
5259
+ `[Pack Test LocalResolver] Test ${test.handle} has invalid rules`
5260
+ );
5218
5261
  return void 0;
5219
5262
  });
5220
5263
  const engineResults = await Promise.all(engineResultsPromises);
5221
- debug2("[Pack Test LocalResolver] All engine results:", JSON.stringify(engineResults));
5222
- const eligibleTests = tests.filter(
5223
- (test, index) => {
5224
- const hasTargetedEvent = !!engineResults?.[index]?.events.find((event) => event.type === "targeted");
5225
- debug2(`[Pack Test LocalResolver] Test ${test.handle} eligibility:`, JSON.stringify({
5264
+ debug2(
5265
+ "[Pack Test LocalResolver] All engine results:",
5266
+ JSON.stringify(engineResults)
5267
+ );
5268
+ const eligibleTests = tests.filter((test, index) => {
5269
+ const hasTargetedEvent = !!engineResults?.[index]?.events.find(
5270
+ (event) => event.type === "targeted"
5271
+ );
5272
+ debug2(
5273
+ `[Pack Test LocalResolver] Test ${test.handle} eligibility:`,
5274
+ JSON.stringify({
5226
5275
  hasTargetedEvent,
5227
5276
  events: engineResults?.[index]?.events
5228
- }));
5229
- return hasTargetedEvent;
5230
- }
5277
+ })
5278
+ );
5279
+ return hasTargetedEvent;
5280
+ });
5281
+ debug2(
5282
+ "[Pack Test LocalResolver] Final eligible tests:",
5283
+ JSON.stringify({
5284
+ eligibleCount: eligibleTests.length,
5285
+ eligibleTests: eligibleTests.map((t) => ({
5286
+ id: t.id,
5287
+ handle: t.handle
5288
+ }))
5289
+ })
5231
5290
  );
5232
- debug2("[Pack Test LocalResolver] Final eligible tests:", JSON.stringify({
5233
- eligibleCount: eligibleTests.length,
5234
- eligibleTests: eligibleTests.map((t) => ({ id: t.id, handle: t.handle }))
5235
- }));
5236
5291
  return eligibleTests;
5237
5292
  }
5238
5293
  /**
@@ -5275,16 +5330,26 @@ var QUERY_TESTS_BY_RULES = `#graphql
5275
5330
  operator
5276
5331
  value
5277
5332
  }
5333
+ impressionTrigger
5278
5334
  testVariants {
5279
5335
  id
5280
5336
  handle
5281
5337
  trafficPercentage
5338
+ sectionTestVariants {
5339
+ section {
5340
+ id
5341
+ }
5342
+ }
5282
5343
  }
5283
5344
  }
5284
5345
  }
5285
5346
  }
5286
5347
  }
5287
5348
  `;
5349
+ function getImpressionSectionIds2(testVariant) {
5350
+ const sectionIds = testVariant?.sectionTestVariants?.map((variant) => variant?.section?.id).filter((id) => Boolean(id)) || [];
5351
+ return [...new Set(sectionIds)];
5352
+ }
5288
5353
  var localTestResolver = new LocalTestResolver();
5289
5354
  function generateTestRulesCacheKey(storeId, contentEnvironment) {
5290
5355
  return `pack-tests:${storeId}:${contentEnvironment || "default"}`;
@@ -5316,7 +5381,7 @@ async function fetchTestRulesShared(packClient, withCache, token) {
5316
5381
  async ({
5317
5382
  addDebugData
5318
5383
  }) => {
5319
- const URL2 = PACK_TEST_CACHE_CHECK_URL;
5384
+ const URL2 = "https://test-cache-check-production.packdigital.workers.dev/tests-updated-at";
5320
5385
  const resp2 = await fetch(URL2, {
5321
5386
  headers: {
5322
5387
  Authorization: `Bearer ${token}`
@@ -5498,13 +5563,16 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5498
5563
  (v) => v.id === currentTestData.testVariant.id
5499
5564
  );
5500
5565
  if (currentVariant) {
5566
+ const impressionSectionIds = getImpressionSectionIds2(currentVariant);
5501
5567
  const refreshedTest = {
5502
5568
  id: freshTestData.id,
5503
5569
  handle: freshTestData.handle,
5570
+ impressionTrigger: freshTestData.impressionTrigger,
5504
5571
  testVariant: {
5505
5572
  id: currentVariant.id,
5506
5573
  handle: currentVariant.handle
5507
5574
  },
5575
+ impression: impressionSectionIds.length > 0 ? { sectionIds: impressionSectionIds } : void 0,
5508
5576
  isFirstExposure: void 0
5509
5577
  };
5510
5578
  const { isFirstExposure: isFirstExposure2, ...testDataWithoutFlag2 } = refreshedTest;
@@ -5552,13 +5620,16 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5552
5620
  (v) => v.id === exposedTest.testVariant?.id
5553
5621
  );
5554
5622
  if (exposedVariant) {
5623
+ const impressionSectionIds = getImpressionSectionIds2(exposedVariant);
5555
5624
  const result2 = {
5556
5625
  id: freshTestData.id,
5557
5626
  handle: freshTestData.handle,
5627
+ impressionTrigger: freshTestData.impressionTrigger,
5558
5628
  testVariant: {
5559
5629
  id: exposedVariant.id,
5560
5630
  handle: exposedVariant.handle
5561
5631
  },
5632
+ impression: impressionSectionIds.length > 0 ? { sectionIds: impressionSectionIds } : void 0,
5562
5633
  isFirstExposure: void 0
5563
5634
  };
5564
5635
  const { isFirstExposure: isFirstExposure2, ...testDataWithoutFlag2 } = result2;
@@ -6045,45 +6116,8 @@ async function getCacheKey(withCache, query, token, options) {
6045
6116
  debug4(`Error getting cache key: ${err}`);
6046
6117
  console.error(err);
6047
6118
  }
6048
- let testsUpdatedAt;
6049
- const hasTestHeaders = options?.headers && (options.headers["X-Pack-Test-Id"] || options.headers["X-Pack-Test-Handle"] || options.headers["X-Pack-Test-Variant-Id"] || options.headers["X-Pack-Test-Variant-Handle"]);
6050
- if (hasTestHeaders) {
6051
- try {
6052
- const resp = await withCache.run(
6053
- {
6054
- cacheKey: ["pack:tests:updatedAt"],
6055
- cacheStrategy: CacheCustom({
6056
- maxAge: 15,
6057
- staleWhileRevalidate: 15
6058
- }),
6059
- shouldCacheResult: (value) => value !== null
6060
- },
6061
- async ({
6062
- addDebugData
6063
- }) => {
6064
- const URL2 = PACK_TEST_CACHE_CHECK_URL;
6065
- const resp2 = await fetch(URL2, {
6066
- headers: { Authorization: `Bearer ${token}` }
6067
- });
6068
- addDebugData?.({
6069
- displayName: "Pack Test Cache Check (content key)",
6070
- response: resp2
6071
- });
6072
- if (resp2.status !== 200) return null;
6073
- return resp2.json();
6074
- }
6075
- );
6076
- testsUpdatedAt = resp?.testsUpdatedAt;
6077
- } catch (err) {
6078
- debug4(`Error getting testsUpdatedAt for cache key: ${err}`);
6079
- }
6080
- }
6081
- if (publishedAt && testsUpdatedAt) {
6082
- return `${queryHash}:${publishedAt}:${testsUpdatedAt}`;
6083
- } else if (publishedAt) {
6119
+ if (publishedAt) {
6084
6120
  return `${queryHash}:${publishedAt}`;
6085
- } else if (testsUpdatedAt) {
6086
- return `${queryHash}:${testsUpdatedAt}`;
6087
6121
  }
6088
6122
  return queryHash;
6089
6123
  }
@@ -6337,20 +6371,6 @@ function createPackClient(options) {
6337
6371
  testInfoForRequest,
6338
6372
  testFromQueryParams: testFromQueryParams || test
6339
6373
  });
6340
- if (testFromQueryParams) {
6341
- try {
6342
- const result = await packClient.fetch(query, {
6343
- variables: queryVariables,
6344
- headers
6345
- });
6346
- return {
6347
- ...result,
6348
- packTestInfo: testInfoForLoader
6349
- };
6350
- } catch (error) {
6351
- return { error, data: null };
6352
- }
6353
- }
6354
6374
  if (previewEnabled) {
6355
6375
  try {
6356
6376
  const result = await packClient.fetch(query, {
@@ -6624,8 +6644,35 @@ var PackTestContext = createContext({});
6624
6644
  var usePackTestContext = () => useContext(PackTestContext);
6625
6645
 
6626
6646
  // src/tests/pack-test-route.ts
6627
- var usePackLoaderData = () => {
6647
+ function getImpressionSectionSelectors(packTestInfo) {
6648
+ const sectionIds = packTestInfo.impression?.sectionIds || [];
6649
+ return [...new Set(sectionIds)].map(
6650
+ (sectionId) => `section[data-comp-id="${sectionId}"]`
6651
+ );
6652
+ }
6653
+ function getImpressionSelectors(packTestInfo, impressionSelector) {
6654
+ if (!impressionSelector) {
6655
+ return getImpressionSectionSelectors(packTestInfo);
6656
+ }
6657
+ const resolvedSelector = typeof impressionSelector === "function" ? impressionSelector(packTestInfo) : impressionSelector;
6658
+ if (!resolvedSelector) {
6659
+ return getImpressionSectionSelectors(packTestInfo);
6660
+ }
6661
+ return Array.isArray(resolvedSelector) ? resolvedSelector : [resolvedSelector];
6662
+ }
6663
+ function serializeExposedTestCookieValue(packTestInfo) {
6664
+ return JSON.stringify({
6665
+ id: packTestInfo.id,
6666
+ handle: packTestInfo.handle,
6667
+ testVariant: {
6668
+ id: packTestInfo.testVariant.id,
6669
+ handle: packTestInfo.testVariant.handle
6670
+ }
6671
+ });
6672
+ }
6673
+ var usePackLoaderData = (impressionSelector) => {
6628
6674
  const [exposedTestInfo, setExposedTestInfo] = useState();
6675
+ const [triggeredExposure, setTriggeredExposure] = useState();
6629
6676
  let packTestInfo;
6630
6677
  let packIsPreviewMode;
6631
6678
  let revalidator;
@@ -6649,61 +6696,216 @@ var usePackLoaderData = () => {
6649
6696
  } = usePackTestContext();
6650
6697
  const exposedTestCookieString = api.get("exposedTest");
6651
6698
  useEffect2(() => {
6652
- if (packTestInfo && !packIsPreviewMode && !hasUserConsent2 && exposedTestCookieString && exposedTestInfo) {
6653
- setPendingExposureQueue?.((prev) => {
6654
- const newQueue = new Map(prev);
6655
- newQueue.set(packTestInfo.id, {
6656
- packTestInfo,
6657
- exposureTime: Date.now()
6699
+ if (packTestInfo?.id !== triggeredExposure?.packTestInfo.id) {
6700
+ setTriggeredExposure(void 0);
6701
+ return;
6702
+ }
6703
+ if (packTestInfo?.testVariant?.id !== triggeredExposure?.packTestInfo.testVariant.id) {
6704
+ setTriggeredExposure(void 0);
6705
+ }
6706
+ }, [triggeredExposure, packTestInfo]);
6707
+ useEffect2(() => {
6708
+ if (!packTestInfo || !!packIsPreviewMode || !!exposedTestCookieString || !!exposedTestInfo) {
6709
+ return;
6710
+ }
6711
+ if (triggeredExposure?.packTestInfo.id === packTestInfo.id && triggeredExposure.packTestInfo.testVariant.id === packTestInfo.testVariant.id) {
6712
+ return;
6713
+ }
6714
+ if (packTestInfo.impressionTrigger !== "ON_ELEMENT_VIEW" || typeof window === "undefined" || typeof IntersectionObserver === "undefined") {
6715
+ setTriggeredExposure({
6716
+ packTestInfo,
6717
+ exposureTime: Date.now()
6718
+ });
6719
+ return;
6720
+ }
6721
+ const impressionSectionSelectors = getImpressionSelectors(
6722
+ packTestInfo,
6723
+ impressionSelector
6724
+ );
6725
+ if (impressionSectionSelectors.length === 0) {
6726
+ console.warn(
6727
+ `[Pack Test] Test "${packTestInfo.id}" uses ON_ELEMENT_VIEW but has no selectors to observe. Impression will not be tracked.`
6728
+ );
6729
+ return;
6730
+ }
6731
+ let isTriggered = false;
6732
+ const observedElements = /* @__PURE__ */ new Set();
6733
+ let observer;
6734
+ let mutationObserver;
6735
+ let observerTimeoutId;
6736
+ const cleanup = () => {
6737
+ observer?.disconnect();
6738
+ mutationObserver?.disconnect();
6739
+ if (observerTimeoutId) {
6740
+ clearTimeout(observerTimeoutId);
6741
+ }
6742
+ };
6743
+ const triggerImpression = () => {
6744
+ if (isTriggered) return;
6745
+ isTriggered = true;
6746
+ cleanup();
6747
+ setTriggeredExposure({
6748
+ packTestInfo,
6749
+ exposureTime: Date.now()
6750
+ });
6751
+ };
6752
+ const observeMatchingElements = () => {
6753
+ if (!observer || isTriggered) return;
6754
+ impressionSectionSelectors.forEach((selector) => {
6755
+ document.querySelectorAll(selector).forEach((element) => {
6756
+ if (observedElements.has(element)) return;
6757
+ observedElements.add(element);
6758
+ observer?.observe(element);
6658
6759
  });
6659
- return newQueue;
6660
6760
  });
6761
+ if (observedElements.size > 0) {
6762
+ mutationObserver?.disconnect();
6763
+ mutationObserver = void 0;
6764
+ if (observerTimeoutId) {
6765
+ clearTimeout(observerTimeoutId);
6766
+ observerTimeoutId = void 0;
6767
+ }
6768
+ }
6769
+ };
6770
+ observer = new IntersectionObserver(
6771
+ (entries) => {
6772
+ if (entries.some((entry) => entry.isIntersecting)) {
6773
+ triggerImpression();
6774
+ }
6775
+ },
6776
+ { threshold: 0.1 }
6777
+ );
6778
+ observeMatchingElements();
6779
+ if (!isTriggered && observedElements.size === 0 && typeof MutationObserver !== "undefined") {
6780
+ mutationObserver = new MutationObserver(() => {
6781
+ observeMatchingElements();
6782
+ });
6783
+ mutationObserver.observe(document.body, {
6784
+ childList: true,
6785
+ subtree: true
6786
+ });
6787
+ observerTimeoutId = setTimeout(() => {
6788
+ if (!isTriggered && observedElements.size === 0) {
6789
+ console.warn(
6790
+ `[Pack Test] Impression target not found within 30s for test "${packTestInfo.id}". Stopping observer.`
6791
+ );
6792
+ cleanup();
6793
+ }
6794
+ }, 3e4);
6661
6795
  }
6796
+ return cleanup;
6662
6797
  }, [
6663
- hasUserConsent2,
6798
+ triggeredExposure,
6799
+ exposedTestCookieString,
6800
+ exposedTestInfo,
6801
+ packIsPreviewMode,
6664
6802
  packTestInfo,
6803
+ impressionSelector
6804
+ ]);
6805
+ useEffect2(() => {
6806
+ if (triggeredExposure && !packIsPreviewMode && !hasUserConsent2 && !exposedTestCookieString) {
6807
+ setPendingExposureQueue?.((prev) => {
6808
+ if (prev.has(triggeredExposure.packTestInfo.id)) return prev;
6809
+ const nextQueue = new Map(prev);
6810
+ nextQueue.set(triggeredExposure.packTestInfo.id, triggeredExposure);
6811
+ return nextQueue;
6812
+ });
6813
+ }
6814
+ }, [
6815
+ triggeredExposure,
6816
+ hasUserConsent2,
6665
6817
  packIsPreviewMode,
6666
6818
  exposedTestCookieString,
6667
- exposedTestInfo
6819
+ setPendingExposureQueue
6668
6820
  ]);
6669
6821
  useEffect2(() => {
6670
- if (packTestInfo && hasUserConsent2 && !exposedTestCookieString && !packIsPreviewMode && !exposedTestInfo) {
6822
+ if (triggeredExposure && hasUserConsent2 && !exposedTestCookieString && !packIsPreviewMode && !exposedTestInfo) {
6823
+ const { packTestInfo: activeTestInfo, exposureTime } = triggeredExposure;
6671
6824
  const expires = /* @__PURE__ */ new Date();
6672
6825
  expires.setHours(expires.getHours() + 24);
6673
- api.set("exposedTest", JSON.stringify(packTestInfo), {
6674
- expires
6675
- });
6676
- setExposedTestInfo(packTestInfo);
6826
+ api.set(
6827
+ "exposedTest",
6828
+ serializeExposedTestCookieValue(activeTestInfo),
6829
+ {
6830
+ expires
6831
+ }
6832
+ );
6833
+ setExposedTestInfo(activeTestInfo);
6677
6834
  pendingExposureQueue?.forEach((data, key) => {
6835
+ if (key === activeTestInfo.id) return;
6678
6836
  try {
6679
6837
  testExposureCallback?.({
6680
6838
  ...data.packTestInfo,
6681
6839
  exposureTime: data.exposureTime
6682
6840
  });
6683
- setPendingExposureQueue?.((prev) => {
6684
- const newQueue = new Map(prev);
6685
- newQueue.delete(key);
6686
- return newQueue;
6687
- });
6688
6841
  } catch (error) {
6689
6842
  console.error("Failed to call testExposure after consent:", error);
6690
6843
  }
6691
6844
  });
6692
- testExposureCallback?.({ ...packTestInfo, exposureTime: Date.now() });
6845
+ testExposureCallback?.({ ...activeTestInfo, exposureTime });
6846
+ setPendingExposureQueue?.(/* @__PURE__ */ new Map());
6693
6847
  setTimeout(() => {
6694
6848
  revalidator?.revalidate();
6695
6849
  }, 750);
6696
6850
  }
6697
6851
  }, [
6698
- packTestInfo,
6852
+ triggeredExposure,
6853
+ exposedTestCookieString,
6854
+ pendingExposureQueue,
6699
6855
  testExposureCallback,
6700
6856
  exposedTestInfo,
6701
6857
  hasUserConsent2,
6702
- packIsPreviewMode
6858
+ packIsPreviewMode,
6859
+ setPendingExposureQueue
6860
+ ]);
6861
+ useEffect2(() => {
6862
+ if (!!triggeredExposure || !hasUserConsent2 || !!packIsPreviewMode || !pendingExposureQueue?.size || !!exposedTestCookieString) {
6863
+ return;
6864
+ }
6865
+ let firstQueuedTest;
6866
+ pendingExposureQueue.forEach((data) => {
6867
+ if (!firstQueuedTest) {
6868
+ firstQueuedTest = data.packTestInfo;
6869
+ }
6870
+ try {
6871
+ testExposureCallback?.({
6872
+ ...data.packTestInfo,
6873
+ exposureTime: data.exposureTime
6874
+ });
6875
+ } catch (error) {
6876
+ console.error(
6877
+ "Failed to call queued testExposure after consent:",
6878
+ error
6879
+ );
6880
+ }
6881
+ });
6882
+ if (firstQueuedTest) {
6883
+ const expires = /* @__PURE__ */ new Date();
6884
+ expires.setHours(expires.getHours() + 24);
6885
+ api.set(
6886
+ "exposedTest",
6887
+ serializeExposedTestCookieValue(firstQueuedTest),
6888
+ {
6889
+ expires
6890
+ }
6891
+ );
6892
+ setExposedTestInfo(firstQueuedTest);
6893
+ }
6894
+ setPendingExposureQueue?.(/* @__PURE__ */ new Map());
6895
+ }, [
6896
+ triggeredExposure,
6897
+ exposedTestCookieString,
6898
+ hasUserConsent2,
6899
+ packIsPreviewMode,
6900
+ pendingExposureQueue,
6901
+ setPendingExposureQueue,
6902
+ testExposureCallback
6703
6903
  ]);
6704
6904
  };
6705
- var PackTestRoute = () => {
6706
- usePackLoaderData();
6905
+ var PackTestRoute = ({
6906
+ impressionSelector
6907
+ } = {}) => {
6908
+ usePackLoaderData(impressionSelector);
6707
6909
  return null;
6708
6910
  };
6709
6911