@pack/hydrogen 2.0.0 → 2.0.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
@@ -5027,6 +5027,18 @@ var LocalTestResolver = class {
5027
5027
  hasRules() {
5028
5028
  return this.testRules.length > 0;
5029
5029
  }
5030
+ /**
5031
+ * Check if a specific test is still active in the current rules
5032
+ */
5033
+ isTestActive(testId) {
5034
+ return this.testRules.some((test) => test.id === testId);
5035
+ }
5036
+ /**
5037
+ * Get fresh test data by ID from current rules
5038
+ */
5039
+ getTestById(testId) {
5040
+ return this.testRules.find((test) => test.id === testId) || null;
5041
+ }
5030
5042
  /**
5031
5043
  * Get random number for test assignment
5032
5044
  */
@@ -5228,11 +5240,6 @@ var LocalTestResolver = class {
5228
5240
  // src/tests/test.ts
5229
5241
  var import_debug3 = __toESM(require_browser(), 1);
5230
5242
  var debug3 = (0, import_debug3.default)("pack:ab-testing:test");
5231
- var QUERY_TEST_VARIANT_IS_RUNNING = `#graphql
5232
- query TestVariantIsRunning($handle: String!, $testId: ID!) {
5233
- testVariantIsRunning(handle: $handle, testId: $testId)
5234
- }
5235
- `;
5236
5243
  var QUERY_TESTS_BY_RULES = `#graphql
5237
5244
  query TestsByRules($status: String) {
5238
5245
  testsByRulesV2(status: $status) {
@@ -5415,14 +5422,15 @@ async function fetchTestRulesShared(packClient, withCache, token) {
5415
5422
  }
5416
5423
  );
5417
5424
  }
5418
- async function packClientFetchTestByRules(packClient, testTargetAudienceAttributes, testSession, request, withCache, token) {
5425
+ async function packClientFetchTestByRules(packClient, testTargetAudienceAttributes, testSession, request, withCache, token, exposedTest, validateOnly) {
5419
5426
  try {
5420
5427
  debug3(
5421
5428
  "[Pack Test] Starting test assignment process:",
5422
5429
  JSON.stringify({
5423
5430
  storeId: packClient.storeId,
5424
5431
  targetingAttributes: testTargetAudienceAttributes,
5425
- requestUrl: request.url
5432
+ requestUrl: request.url,
5433
+ validateOnly
5426
5434
  })
5427
5435
  );
5428
5436
  if (!packClient.storeId) {
@@ -5448,6 +5456,65 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5448
5456
  debug3(
5449
5457
  "[Pack Test] Updated local resolver with rules from shared cache"
5450
5458
  );
5459
+ const currentTestData = testSession.getTestData();
5460
+ let needsNewTest = false;
5461
+ if (currentTestData?.id) {
5462
+ const freshTestData = localTestResolver.getTestById(currentTestData.id);
5463
+ if (!freshTestData) {
5464
+ debug3(
5465
+ "[Pack Test] Current session test is no longer active (paused/stopped), clearing session:",
5466
+ JSON.stringify({
5467
+ testId: currentTestData.id,
5468
+ testHandle: currentTestData.handle
5469
+ })
5470
+ );
5471
+ testSession.clearTestData();
5472
+ needsNewTest = true;
5473
+ } else {
5474
+ debug3(
5475
+ "[Pack Test] Current session test is still active in rules, updating with fresh data:",
5476
+ JSON.stringify({
5477
+ testId: freshTestData.id,
5478
+ testHandle: freshTestData.handle,
5479
+ variantsCount: freshTestData.testVariants?.length
5480
+ })
5481
+ );
5482
+ if (currentTestData.testVariant?.id) {
5483
+ const currentVariant = freshTestData.testVariants?.find(
5484
+ (v) => v.id === currentTestData.testVariant.id
5485
+ );
5486
+ if (currentVariant) {
5487
+ const refreshedTest = {
5488
+ id: freshTestData.id,
5489
+ handle: freshTestData.handle,
5490
+ testVariant: {
5491
+ id: currentVariant.id,
5492
+ handle: currentVariant.handle
5493
+ },
5494
+ isFirstExposure: void 0
5495
+ };
5496
+ const { isFirstExposure: isFirstExposure2, ...testDataWithoutFlag2 } = refreshedTest;
5497
+ testSession.setTestData(testDataWithoutFlag2);
5498
+ testSession.setExpireAt(getExpireAtDate().toISOString());
5499
+ return refreshedTest;
5500
+ } else {
5501
+ debug3(
5502
+ "[Pack Test] Current variant no longer exists in test, clearing session:",
5503
+ JSON.stringify({
5504
+ testId: currentTestData.id,
5505
+ variantId: currentTestData.testVariant.id
5506
+ })
5507
+ );
5508
+ testSession.clearTestData();
5509
+ needsNewTest = true;
5510
+ }
5511
+ }
5512
+ }
5513
+ }
5514
+ if (validateOnly && !needsNewTest) {
5515
+ debug3("[Pack Test] Validation complete, no new test needed");
5516
+ return void 0;
5517
+ }
5451
5518
  } catch (error) {
5452
5519
  debug3(
5453
5520
  "[Pack Test] Failed to fetch test rules:",
@@ -5460,6 +5527,51 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5460
5527
  "[Pack Test] No withCache or token available - cannot fetch test rules"
5461
5528
  );
5462
5529
  }
5530
+ if (exposedTest) {
5531
+ const freshTestData = localTestResolver.getTestById(exposedTest.id);
5532
+ if (freshTestData) {
5533
+ debug3(
5534
+ "[Pack Test] Exposed test is still active in rules, restoring with fresh data:",
5535
+ JSON.stringify({
5536
+ testId: freshTestData.id,
5537
+ testHandle: freshTestData.handle
5538
+ })
5539
+ );
5540
+ const exposedVariant = freshTestData.testVariants?.find(
5541
+ (v) => v.id === exposedTest.testVariant?.id
5542
+ );
5543
+ if (exposedVariant) {
5544
+ const result2 = {
5545
+ id: freshTestData.id,
5546
+ handle: freshTestData.handle,
5547
+ testVariant: {
5548
+ id: exposedVariant.id,
5549
+ handle: exposedVariant.handle
5550
+ },
5551
+ isFirstExposure: void 0
5552
+ };
5553
+ const { isFirstExposure: isFirstExposure2, ...testDataWithoutFlag2 } = result2;
5554
+ testSession.setTestData(testDataWithoutFlag2);
5555
+ testSession.setExpireAt(getExpireAtDate().toISOString());
5556
+ return result2;
5557
+ } else {
5558
+ debug3(
5559
+ "[Pack Test] Exposed test variant no longer exists in fresh data:",
5560
+ JSON.stringify({
5561
+ testId: exposedTest.id,
5562
+ variantId: exposedTest.testVariant?.id
5563
+ })
5564
+ );
5565
+ }
5566
+ } else {
5567
+ debug3(
5568
+ "[Pack Test] Exposed test is no longer active in rules:",
5569
+ JSON.stringify({
5570
+ testId: exposedTest.id
5571
+ })
5572
+ );
5573
+ }
5574
+ }
5463
5575
  const sessionId = packClient.storeId;
5464
5576
  debug3("[Pack Test] Assigning test with session ID:", sessionId);
5465
5577
  const assignedTest = await localTestResolver.assignTest(
@@ -5512,46 +5624,6 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5512
5624
  return void 0;
5513
5625
  }
5514
5626
  }
5515
- async function packClientCheckTestIsRunning(packClient, testSession) {
5516
- debug3(
5517
- "[Pack Test] Checking if test variant is running:",
5518
- JSON.stringify({
5519
- testId: testSession.id,
5520
- testHandle: testSession.handle,
5521
- variantId: testSession.testVariant.id,
5522
- variantHandle: testSession.testVariant.handle
5523
- })
5524
- );
5525
- try {
5526
- const resp = await packClient.fetch(QUERY_TEST_VARIANT_IS_RUNNING, {
5527
- variables: {
5528
- handle: testSession.testVariant.handle,
5529
- testId: testSession.id
5530
- }
5531
- });
5532
- const isRunning = !!resp.data.testVariantIsRunning;
5533
- debug3(
5534
- "[Pack Test] Test variant running check result:",
5535
- JSON.stringify({
5536
- testId: testSession.id,
5537
- variantHandle: testSession.testVariant.handle,
5538
- isRunning,
5539
- responseData: resp.data
5540
- })
5541
- );
5542
- return isRunning;
5543
- } catch (e) {
5544
- debug3(
5545
- "[Pack Test] Error checking test variant status:",
5546
- JSON.stringify({
5547
- testId: testSession.id,
5548
- variantHandle: testSession.testVariant.handle,
5549
- error: e instanceof Error ? e.message : e
5550
- })
5551
- );
5552
- }
5553
- return false;
5554
- }
5555
5627
  function getExpireAtDate() {
5556
5628
  const expireAt = /* @__PURE__ */ new Date();
5557
5629
  expireAt.setHours(expireAt.getHours() + 4);
@@ -5606,63 +5678,68 @@ async function getTestInfo({
5606
5678
  debug3("[Pack Test] Found existing test data in session");
5607
5679
  if (!testSessionExpireAt || new Date(testSessionExpireAt) >= /* @__PURE__ */ new Date()) {
5608
5680
  debug3(
5609
- "[Pack Test] Test session not expired, checking if test is still running"
5681
+ "[Pack Test] Test session not expired by time, fetching rules to validate if test is still active"
5610
5682
  );
5611
5683
  try {
5612
- const testStillRunning = await packClientCheckTestIsRunning(
5684
+ const validationResult = await packClientFetchTestByRules(
5613
5685
  packClient,
5614
- testSessionData
5615
- );
5616
- debug3(
5617
- "[Pack Test] Test running check result:",
5618
- JSON.stringify({
5619
- testId: testSessionData.id,
5620
- isRunning: testStillRunning
5621
- })
5686
+ testTargetAudienceAttributes,
5687
+ testSession,
5688
+ request,
5689
+ withCache,
5690
+ token,
5691
+ null,
5692
+ true
5693
+ // validateOnly - but will assign new test if current is inactive
5622
5694
  );
5623
- if (testStillRunning) {
5624
- debug3(
5625
- "[Pack Test] Using existing valid test session data",
5626
- JSON.stringify({
5627
- testId: testSessionData.id,
5628
- hasIsFirstExposure: "isFirstExposure" in testSessionData,
5629
- isFirstExposure: testSessionData.isFirstExposure
5630
- })
5631
- );
5632
- const cleanTestInfo = {
5633
- ...testSessionData,
5634
- isFirstExposure: void 0
5635
- };
5636
- debug3(
5637
- "[Pack Test] Returning cleaned test info:",
5638
- JSON.stringify({
5639
- testId: cleanTestInfo.id,
5640
- hasIsFirstExposure: "isFirstExposure" in cleanTestInfo,
5641
- isFirstExposure: cleanTestInfo.isFirstExposure
5642
- })
5643
- );
5644
- return cleanTestInfo;
5695
+ if (validationResult) {
5696
+ if (validationResult.id === testSessionData.id) {
5697
+ debug3(
5698
+ "[Pack Test] Test is still active, using refreshed data",
5699
+ JSON.stringify({
5700
+ testId: validationResult.id,
5701
+ testHandle: validationResult.handle,
5702
+ variantId: validationResult.testVariant.id,
5703
+ variantHandle: validationResult.testVariant.handle
5704
+ })
5705
+ );
5706
+ return validationResult;
5707
+ } else {
5708
+ debug3(
5709
+ "[Pack Test] Original test no longer active, new test assigned",
5710
+ JSON.stringify({
5711
+ oldTestId: testSessionData.id,
5712
+ newTestId: validationResult.id,
5713
+ newTestHandle: validationResult.handle,
5714
+ newVariantId: validationResult.testVariant.id,
5715
+ newVariantHandle: validationResult.testVariant.handle
5716
+ })
5717
+ );
5718
+ return {
5719
+ ...validationResult,
5720
+ isFirstExposure: true
5721
+ };
5722
+ }
5645
5723
  } else {
5646
- debug3(
5647
- "[Pack Test] Test is no longer running, will fetch new test"
5648
- );
5724
+ debug3("[Pack Test] Test is no longer active and no new test was assigned");
5725
+ return void 0;
5649
5726
  }
5650
5727
  } catch (error) {
5651
5728
  debug3(
5652
- "[Pack Test] Error checking if test is running, using cached data:",
5729
+ "[Pack Test] Error validating test, using cached data:",
5653
5730
  JSON.stringify(error)
5654
5731
  );
5655
5732
  console.error(
5656
- "[Pack Test] Error checking if test is running, using cached data:",
5733
+ "[Pack Test] Error validating test, using cached data:",
5657
5734
  error
5658
5735
  );
5659
5736
  return { ...testSessionData, isFirstExposure: void 0 };
5660
5737
  }
5661
5738
  } else {
5662
- debug3("[Pack Test] Test session has expired");
5739
+ debug3("[Pack Test] Test session has expired by time");
5740
+ debug3("[Pack Test] Clearing expired test session data");
5741
+ testSession.clearTestData();
5663
5742
  }
5664
- debug3("[Pack Test] Clearing expired/invalid test session data");
5665
- testSession.clearTestData();
5666
5743
  }
5667
5744
  if (exposedTestCookieString) {
5668
5745
  try {
@@ -5686,72 +5763,47 @@ async function getTestInfo({
5686
5763
  }
5687
5764
  if (exposedTest && !testSessionData) {
5688
5765
  debug3(
5689
- "[Pack Test] Found exposed test cookie with no session data - checking if test is still running"
5766
+ "[Pack Test] Found exposed test cookie with no session data - will fetch rules and validate"
5690
5767
  );
5691
- let testVariantIsRunning = false;
5692
5768
  try {
5693
- testVariantIsRunning = await packClientCheckTestIsRunning(
5769
+ testInfo = await packClientFetchTestByRules(
5694
5770
  packClient,
5695
- exposedTest
5771
+ testTargetAudienceAttributes,
5772
+ testSession,
5773
+ request,
5774
+ withCache,
5775
+ token,
5776
+ exposedTest,
5777
+ false
5778
+ // Not validateOnly - allow assigning new tests
5696
5779
  );
5780
+ if (testInfo?.id === exposedTest.id) {
5781
+ debug3(
5782
+ "[Pack Test] Exposed test was restored (still active)"
5783
+ );
5784
+ } else if (testInfo) {
5785
+ debug3(
5786
+ "[Pack Test] Exposed test is no longer active, using newly assigned test:",
5787
+ JSON.stringify({
5788
+ exposedTestId: exposedTest.id,
5789
+ newTestId: testInfo.id
5790
+ })
5791
+ );
5792
+ } else {
5793
+ debug3(
5794
+ "[Pack Test] Exposed test is no longer active and no new test was assigned"
5795
+ );
5796
+ }
5697
5797
  } catch (error) {
5698
5798
  debug3(
5699
- "[Pack Test] Error checking if test is still running:",
5799
+ "[Pack Test] Error processing exposed test:",
5700
5800
  JSON.stringify(error)
5701
5801
  );
5702
5802
  console.error(
5703
- "[Pack Test] Error checking if test is still running, assuming not running:",
5803
+ "[Pack Test] Error processing exposed test, continuing without test:",
5704
5804
  error
5705
5805
  );
5706
- testVariantIsRunning = false;
5707
- }
5708
- debug3(
5709
- "[Pack Test] Exposed test running check:",
5710
- JSON.stringify({
5711
- testId: exposedTest.id,
5712
- isRunning: testVariantIsRunning
5713
- })
5714
- );
5715
- if (testVariantIsRunning) {
5716
- const { id, handle, testVariant } = exposedTest;
5717
- testInfo = {
5718
- id,
5719
- handle,
5720
- testVariant,
5721
- isFirstExposure: void 0
5722
- };
5723
- debug3(
5724
- "[Pack Test] Restoring test from cookie to session:",
5725
- JSON.stringify(testInfo)
5726
- );
5727
- const { isFirstExposure, ...testDataWithoutFlag } = testInfo;
5728
- testSession.setTestData(testDataWithoutFlag);
5729
- testSession.setExpireAt(getExpireAtDate().toISOString());
5730
- } else {
5731
- debug3(
5732
- "[Pack Test] Exposed test is no longer running, clearing session and fetching new test"
5733
- );
5734
- testSession.clearTestData();
5735
- try {
5736
- testInfo = await packClientFetchTestByRules(
5737
- packClient,
5738
- testTargetAudienceAttributes,
5739
- testSession,
5740
- request,
5741
- withCache,
5742
- token
5743
- );
5744
- } catch (error) {
5745
- debug3(
5746
- "[Pack Test] Error fetching new test after exposed test expired:",
5747
- JSON.stringify(error)
5748
- );
5749
- console.error(
5750
- "[Pack Test] Error fetching new test, continuing without test:",
5751
- error
5752
- );
5753
- testInfo = void 0;
5754
- }
5806
+ testInfo = void 0;
5755
5807
  }
5756
5808
  } else if (testSessionData) {
5757
5809
  debug3(
@@ -5765,7 +5817,10 @@ async function getTestInfo({
5765
5817
  testSession,
5766
5818
  request,
5767
5819
  withCache,
5768
- token
5820
+ token,
5821
+ null,
5822
+ false
5823
+ // Not validateOnly - allow assigning new tests
5769
5824
  );
5770
5825
  } catch (error) {
5771
5826
  debug3(
@@ -5789,7 +5844,10 @@ async function getTestInfo({
5789
5844
  testSession,
5790
5845
  request,
5791
5846
  withCache,
5792
- token
5847
+ token,
5848
+ null,
5849
+ false
5850
+ // Not validateOnly - allow assigning new tests
5793
5851
  );
5794
5852
  } catch (error) {
5795
5853
  debug3(