@pack/hydrogen 3.2.8 → 3.3.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
@@ -32,6 +32,172 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  ));
33
33
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
34
 
35
+ // node_modules/cookie/index.js
36
+ var require_cookie = __commonJS({
37
+ "node_modules/cookie/index.js"(exports) {
38
+ "use strict";
39
+ exports.parse = parse3;
40
+ exports.serialize = serialize3;
41
+ var __toString = Object.prototype.toString;
42
+ var __hasOwnProperty = Object.prototype.hasOwnProperty;
43
+ var cookieNameRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
44
+ var cookieValueRegExp = /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/;
45
+ var domainValueRegExp = /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
46
+ var pathValueRegExp = /^[\u0020-\u003A\u003D-\u007E]*$/;
47
+ function parse3(str, opt) {
48
+ if (typeof str !== "string") {
49
+ throw new TypeError("argument str must be a string");
50
+ }
51
+ var obj = {};
52
+ var len = str.length;
53
+ if (len < 2) return obj;
54
+ var dec = opt && opt.decode || decode;
55
+ var index = 0;
56
+ var eqIdx = 0;
57
+ var endIdx = 0;
58
+ do {
59
+ eqIdx = str.indexOf("=", index);
60
+ if (eqIdx === -1) break;
61
+ endIdx = str.indexOf(";", index);
62
+ if (endIdx === -1) {
63
+ endIdx = len;
64
+ } else if (eqIdx > endIdx) {
65
+ index = str.lastIndexOf(";", eqIdx - 1) + 1;
66
+ continue;
67
+ }
68
+ var keyStartIdx = startIndex(str, index, eqIdx);
69
+ var keyEndIdx = endIndex(str, eqIdx, keyStartIdx);
70
+ var key = str.slice(keyStartIdx, keyEndIdx);
71
+ if (!__hasOwnProperty.call(obj, key)) {
72
+ var valStartIdx = startIndex(str, eqIdx + 1, endIdx);
73
+ var valEndIdx = endIndex(str, endIdx, valStartIdx);
74
+ if (str.charCodeAt(valStartIdx) === 34 && str.charCodeAt(valEndIdx - 1) === 34) {
75
+ valStartIdx++;
76
+ valEndIdx--;
77
+ }
78
+ var val = str.slice(valStartIdx, valEndIdx);
79
+ obj[key] = tryDecode(val, dec);
80
+ }
81
+ index = endIdx + 1;
82
+ } while (index < len);
83
+ return obj;
84
+ }
85
+ function startIndex(str, index, max) {
86
+ do {
87
+ var code = str.charCodeAt(index);
88
+ if (code !== 32 && code !== 9) return index;
89
+ } while (++index < max);
90
+ return max;
91
+ }
92
+ function endIndex(str, index, min) {
93
+ while (index > min) {
94
+ var code = str.charCodeAt(--index);
95
+ if (code !== 32 && code !== 9) return index + 1;
96
+ }
97
+ return min;
98
+ }
99
+ function serialize3(name, val, opt) {
100
+ var enc = opt && opt.encode || encodeURIComponent;
101
+ if (typeof enc !== "function") {
102
+ throw new TypeError("option encode is invalid");
103
+ }
104
+ if (!cookieNameRegExp.test(name)) {
105
+ throw new TypeError("argument name is invalid");
106
+ }
107
+ var value = enc(val);
108
+ if (!cookieValueRegExp.test(value)) {
109
+ throw new TypeError("argument val is invalid");
110
+ }
111
+ var str = name + "=" + value;
112
+ if (!opt) return str;
113
+ if (null != opt.maxAge) {
114
+ var maxAge = Math.floor(opt.maxAge);
115
+ if (!isFinite(maxAge)) {
116
+ throw new TypeError("option maxAge is invalid");
117
+ }
118
+ str += "; Max-Age=" + maxAge;
119
+ }
120
+ if (opt.domain) {
121
+ if (!domainValueRegExp.test(opt.domain)) {
122
+ throw new TypeError("option domain is invalid");
123
+ }
124
+ str += "; Domain=" + opt.domain;
125
+ }
126
+ if (opt.path) {
127
+ if (!pathValueRegExp.test(opt.path)) {
128
+ throw new TypeError("option path is invalid");
129
+ }
130
+ str += "; Path=" + opt.path;
131
+ }
132
+ if (opt.expires) {
133
+ var expires = opt.expires;
134
+ if (!isDate(expires) || isNaN(expires.valueOf())) {
135
+ throw new TypeError("option expires is invalid");
136
+ }
137
+ str += "; Expires=" + expires.toUTCString();
138
+ }
139
+ if (opt.httpOnly) {
140
+ str += "; HttpOnly";
141
+ }
142
+ if (opt.secure) {
143
+ str += "; Secure";
144
+ }
145
+ if (opt.partitioned) {
146
+ str += "; Partitioned";
147
+ }
148
+ if (opt.priority) {
149
+ var priority = typeof opt.priority === "string" ? opt.priority.toLowerCase() : opt.priority;
150
+ switch (priority) {
151
+ case "low":
152
+ str += "; Priority=Low";
153
+ break;
154
+ case "medium":
155
+ str += "; Priority=Medium";
156
+ break;
157
+ case "high":
158
+ str += "; Priority=High";
159
+ break;
160
+ default:
161
+ throw new TypeError("option priority is invalid");
162
+ }
163
+ }
164
+ if (opt.sameSite) {
165
+ var sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
166
+ switch (sameSite) {
167
+ case true:
168
+ str += "; SameSite=Strict";
169
+ break;
170
+ case "lax":
171
+ str += "; SameSite=Lax";
172
+ break;
173
+ case "strict":
174
+ str += "; SameSite=Strict";
175
+ break;
176
+ case "none":
177
+ str += "; SameSite=None";
178
+ break;
179
+ default:
180
+ throw new TypeError("option sameSite is invalid");
181
+ }
182
+ }
183
+ return str;
184
+ }
185
+ function decode(str) {
186
+ return str.indexOf("%") !== -1 ? decodeURIComponent(str) : str;
187
+ }
188
+ function isDate(val) {
189
+ return __toString.call(val) === "[object Date]";
190
+ }
191
+ function tryDecode(str, decode2) {
192
+ try {
193
+ return decode2(str);
194
+ } catch (e) {
195
+ return str;
196
+ }
197
+ }
198
+ }
199
+ });
200
+
35
201
  // ../../node_modules/ua-parser-js/src/ua-parser.js
36
202
  var require_ua_parser = __commonJS({
37
203
  "../../node_modules/ua-parser-js/src/ua-parser.js"(exports, module) {
@@ -1283,203 +1449,37 @@ var require_ua_parser = __commonJS({
1283
1449
  UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]);
1284
1450
  UAParser.CPU = enumerize([ARCHITECTURE]);
1285
1451
  UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);
1286
- UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);
1287
- if (typeof exports !== UNDEF_TYPE) {
1288
- if (typeof module !== UNDEF_TYPE && module.exports) {
1289
- exports = module.exports = UAParser;
1290
- }
1291
- exports.UAParser = UAParser;
1292
- } else {
1293
- if (typeof define === FUNC_TYPE && define.amd) {
1294
- define(function() {
1295
- return UAParser;
1296
- });
1297
- } else if (typeof window2 !== UNDEF_TYPE) {
1298
- window2.UAParser = UAParser;
1299
- }
1300
- }
1301
- var $ = typeof window2 !== UNDEF_TYPE && (window2.jQuery || window2.Zepto);
1302
- if ($ && !$.ua) {
1303
- var parser2 = new UAParser();
1304
- $.ua = parser2.getResult();
1305
- $.ua.get = function() {
1306
- return parser2.getUA();
1307
- };
1308
- $.ua.set = function(ua) {
1309
- parser2.setUA(ua);
1310
- var result = parser2.getResult();
1311
- for (var prop in result) {
1312
- $.ua[prop] = result[prop];
1313
- }
1314
- };
1315
- }
1316
- })(typeof window === "object" ? window : exports);
1317
- }
1318
- });
1319
-
1320
- // node_modules/cookie/index.js
1321
- var require_cookie = __commonJS({
1322
- "node_modules/cookie/index.js"(exports) {
1323
- "use strict";
1324
- exports.parse = parse2;
1325
- exports.serialize = serialize3;
1326
- var __toString = Object.prototype.toString;
1327
- var __hasOwnProperty = Object.prototype.hasOwnProperty;
1328
- var cookieNameRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
1329
- var cookieValueRegExp = /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/;
1330
- var domainValueRegExp = /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
1331
- var pathValueRegExp = /^[\u0020-\u003A\u003D-\u007E]*$/;
1332
- function parse2(str, opt) {
1333
- if (typeof str !== "string") {
1334
- throw new TypeError("argument str must be a string");
1335
- }
1336
- var obj = {};
1337
- var len = str.length;
1338
- if (len < 2) return obj;
1339
- var dec = opt && opt.decode || decode;
1340
- var index = 0;
1341
- var eqIdx = 0;
1342
- var endIdx = 0;
1343
- do {
1344
- eqIdx = str.indexOf("=", index);
1345
- if (eqIdx === -1) break;
1346
- endIdx = str.indexOf(";", index);
1347
- if (endIdx === -1) {
1348
- endIdx = len;
1349
- } else if (eqIdx > endIdx) {
1350
- index = str.lastIndexOf(";", eqIdx - 1) + 1;
1351
- continue;
1352
- }
1353
- var keyStartIdx = startIndex(str, index, eqIdx);
1354
- var keyEndIdx = endIndex(str, eqIdx, keyStartIdx);
1355
- var key = str.slice(keyStartIdx, keyEndIdx);
1356
- if (!__hasOwnProperty.call(obj, key)) {
1357
- var valStartIdx = startIndex(str, eqIdx + 1, endIdx);
1358
- var valEndIdx = endIndex(str, endIdx, valStartIdx);
1359
- if (str.charCodeAt(valStartIdx) === 34 && str.charCodeAt(valEndIdx - 1) === 34) {
1360
- valStartIdx++;
1361
- valEndIdx--;
1362
- }
1363
- var val = str.slice(valStartIdx, valEndIdx);
1364
- obj[key] = tryDecode(val, dec);
1365
- }
1366
- index = endIdx + 1;
1367
- } while (index < len);
1368
- return obj;
1369
- }
1370
- function startIndex(str, index, max) {
1371
- do {
1372
- var code = str.charCodeAt(index);
1373
- if (code !== 32 && code !== 9) return index;
1374
- } while (++index < max);
1375
- return max;
1376
- }
1377
- function endIndex(str, index, min) {
1378
- while (index > min) {
1379
- var code = str.charCodeAt(--index);
1380
- if (code !== 32 && code !== 9) return index + 1;
1381
- }
1382
- return min;
1383
- }
1384
- function serialize3(name, val, opt) {
1385
- var enc = opt && opt.encode || encodeURIComponent;
1386
- if (typeof enc !== "function") {
1387
- throw new TypeError("option encode is invalid");
1388
- }
1389
- if (!cookieNameRegExp.test(name)) {
1390
- throw new TypeError("argument name is invalid");
1391
- }
1392
- var value = enc(val);
1393
- if (!cookieValueRegExp.test(value)) {
1394
- throw new TypeError("argument val is invalid");
1395
- }
1396
- var str = name + "=" + value;
1397
- if (!opt) return str;
1398
- if (null != opt.maxAge) {
1399
- var maxAge = Math.floor(opt.maxAge);
1400
- if (!isFinite(maxAge)) {
1401
- throw new TypeError("option maxAge is invalid");
1402
- }
1403
- str += "; Max-Age=" + maxAge;
1404
- }
1405
- if (opt.domain) {
1406
- if (!domainValueRegExp.test(opt.domain)) {
1407
- throw new TypeError("option domain is invalid");
1408
- }
1409
- str += "; Domain=" + opt.domain;
1410
- }
1411
- if (opt.path) {
1412
- if (!pathValueRegExp.test(opt.path)) {
1413
- throw new TypeError("option path is invalid");
1414
- }
1415
- str += "; Path=" + opt.path;
1416
- }
1417
- if (opt.expires) {
1418
- var expires = opt.expires;
1419
- if (!isDate(expires) || isNaN(expires.valueOf())) {
1420
- throw new TypeError("option expires is invalid");
1421
- }
1422
- str += "; Expires=" + expires.toUTCString();
1423
- }
1424
- if (opt.httpOnly) {
1425
- str += "; HttpOnly";
1426
- }
1427
- if (opt.secure) {
1428
- str += "; Secure";
1429
- }
1430
- if (opt.partitioned) {
1431
- str += "; Partitioned";
1432
- }
1433
- if (opt.priority) {
1434
- var priority = typeof opt.priority === "string" ? opt.priority.toLowerCase() : opt.priority;
1435
- switch (priority) {
1436
- case "low":
1437
- str += "; Priority=Low";
1438
- break;
1439
- case "medium":
1440
- str += "; Priority=Medium";
1441
- break;
1442
- case "high":
1443
- str += "; Priority=High";
1444
- break;
1445
- default:
1446
- throw new TypeError("option priority is invalid");
1452
+ UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);
1453
+ if (typeof exports !== UNDEF_TYPE) {
1454
+ if (typeof module !== UNDEF_TYPE && module.exports) {
1455
+ exports = module.exports = UAParser;
1447
1456
  }
1448
- }
1449
- if (opt.sameSite) {
1450
- var sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
1451
- switch (sameSite) {
1452
- case true:
1453
- str += "; SameSite=Strict";
1454
- break;
1455
- case "lax":
1456
- str += "; SameSite=Lax";
1457
- break;
1458
- case "strict":
1459
- str += "; SameSite=Strict";
1460
- break;
1461
- case "none":
1462
- str += "; SameSite=None";
1463
- break;
1464
- default:
1465
- throw new TypeError("option sameSite is invalid");
1457
+ exports.UAParser = UAParser;
1458
+ } else {
1459
+ if (typeof define === FUNC_TYPE && define.amd) {
1460
+ define(function() {
1461
+ return UAParser;
1462
+ });
1463
+ } else if (typeof window2 !== UNDEF_TYPE) {
1464
+ window2.UAParser = UAParser;
1466
1465
  }
1467
1466
  }
1468
- return str;
1469
- }
1470
- function decode(str) {
1471
- return str.indexOf("%") !== -1 ? decodeURIComponent(str) : str;
1472
- }
1473
- function isDate(val) {
1474
- return __toString.call(val) === "[object Date]";
1475
- }
1476
- function tryDecode(str, decode2) {
1477
- try {
1478
- return decode2(str);
1479
- } catch (e) {
1480
- return str;
1467
+ var $ = typeof window2 !== UNDEF_TYPE && (window2.jQuery || window2.Zepto);
1468
+ if ($ && !$.ua) {
1469
+ var parser2 = new UAParser();
1470
+ $.ua = parser2.getResult();
1471
+ $.ua.get = function() {
1472
+ return parser2.getUA();
1473
+ };
1474
+ $.ua.set = function(ua) {
1475
+ parser2.setUA(ua);
1476
+ var result = parser2.getResult();
1477
+ for (var prop in result) {
1478
+ $.ua[prop] = result[prop];
1479
+ }
1480
+ };
1481
1481
  }
1482
- }
1482
+ })(typeof window === "object" ? window : exports);
1483
1483
  }
1484
1484
  });
1485
1485
 
@@ -1497,7 +1497,7 @@ var require_ms = __commonJS({
1497
1497
  options = options || {};
1498
1498
  var type = typeof val;
1499
1499
  if (type === "string" && val.length > 0) {
1500
- return parse2(val);
1500
+ return parse3(val);
1501
1501
  } else if (type === "number" && isFinite(val)) {
1502
1502
  return options.long ? fmtLong(val) : fmtShort(val);
1503
1503
  }
@@ -1505,7 +1505,7 @@ var require_ms = __commonJS({
1505
1505
  "val is not a non-empty string or a valid number. val=" + JSON.stringify(val)
1506
1506
  );
1507
1507
  };
1508
- function parse2(str) {
1508
+ function parse3(str) {
1509
1509
  str = String(str);
1510
1510
  if (str.length > 100) {
1511
1511
  return;
@@ -4356,6 +4356,9 @@ var require_dist = __commonJS({
4356
4356
  }
4357
4357
  });
4358
4358
 
4359
+ // src/handle-request.ts
4360
+ var import_cookie2 = __toESM(require_cookie(), 1);
4361
+
4359
4362
  // ../packlytics/dist/utils/get-packlytics-id.js
4360
4363
  function sha256(ascii) {
4361
4364
  function rightRotate(value, amount) {
@@ -4667,64 +4670,23 @@ async function packlytics(pack, request, next) {
4667
4670
  return next();
4668
4671
  }
4669
4672
 
4670
- // src/handle-request.ts
4671
- async function handleRequest(pack, request, handleRequest2) {
4672
- const packHandleResponse = await pack.handleRequest(request);
4673
- let response;
4674
- response = await packlytics(pack, request, () => {
4675
- return handleRequest2(request);
4676
- });
4677
- packHandleResponse(response);
4678
- response.headers.append("powered-by", "Shopify, Hydrogen + Pack Digital");
4679
- response.headers.append("Set-Cookie", await pack.session.commit());
4680
- if (pack.testSession && pack.testSession.hasChanges()) {
4681
- response.headers.append("Set-Cookie", await pack.testSession.commit());
4682
- }
4683
- return response;
4684
- }
4685
-
4686
- // src/session/usePackCookies.ts
4687
- var import_cookie = __toESM(require_cookie(), 1);
4688
- import { useEffect } from "react";
4689
-
4690
4673
  // src/constants.ts
4691
4674
  var PACK_COOKIE_ID = "__pack";
4692
4675
  var PACK_USER_CONSENT_COOKIE_ID = "__pack_user_consent";
4693
4676
  var PACK_TEST_COOKIE_ID = "__pack_test";
4694
4677
  var PACK_COOKIE_MAX_AGE = 60 * 60 * 24 * 360;
4695
-
4696
- // src/session/usePackCookies.ts
4697
- function usePackCookies(options) {
4698
- const { hasUserConsent: hasUserConsent2 = true, domain = "" } = options || {};
4699
- useEffect(() => {
4700
- setCookie(
4701
- PACK_USER_CONSENT_COOKIE_ID,
4702
- hasUserConsent2 ? "true" : "false",
4703
- PACK_COOKIE_MAX_AGE,
4704
- domain
4705
- );
4706
- }, [hasUserConsent2]);
4707
- }
4708
- function setCookie(name, value, maxage, domain) {
4709
- document.cookie = (0, import_cookie.serialize)(name, value, {
4710
- maxAge: maxage,
4711
- domain,
4712
- sameSite: "lax",
4713
- path: "/"
4714
- });
4715
- }
4716
-
4717
- // src/session/session.ts
4718
- import {
4719
- createCookie,
4720
- createCookieSessionStorage
4721
- } from "react-router";
4678
+ var PACK_SESSION_COOKIE_ID = "__pack_s";
4679
+ var PACK_SESSION_MAX_AGE = 30 * 60;
4722
4680
 
4723
4681
  // src/session/cookies-utils.tsx
4724
- var import_cookie2 = __toESM(require_cookie(), 1);
4682
+ var import_cookie = __toESM(require_cookie(), 1);
4725
4683
  var tokenHash = "xxxx-4xxx-xxxx-xxxxxxxxxxxx";
4684
+ function isSafari(userAgent) {
4685
+ if (!userAgent) return false;
4686
+ return /Safari\//.test(userAgent) && !/Chrome\//.test(userAgent) && !/Chromium\//.test(userAgent) && !/Edg\//.test(userAgent) && !/Firefox\//.test(userAgent);
4687
+ }
4726
4688
  function hasUserConsent(request) {
4727
- const cookies = import_cookie2.default.parse(request.headers.get("Cookie"));
4689
+ const cookies = import_cookie.default.parse(request.headers.get("Cookie") || "");
4728
4690
  return cookies[PACK_USER_CONSENT_COOKIE_ID] ? cookies[PACK_USER_CONSENT_COOKIE_ID] === "true" : true;
4729
4691
  }
4730
4692
  function buildRandomUUID() {
@@ -4762,11 +4724,72 @@ function hexTime() {
4762
4724
  return output?.substring(0, 8).padStart(8, "0");
4763
4725
  }
4764
4726
 
4765
- // src/session/session.ts
4766
- function isSafari(userAgent) {
4767
- if (!userAgent) return false;
4768
- return /Safari\//.test(userAgent) && !/Chrome\//.test(userAgent) && !/Chromium\//.test(userAgent) && !/Edg\//.test(userAgent) && !/Firefox\//.test(userAgent);
4727
+ // src/audiences/visitor-recency.ts
4728
+ var PRIOR_VISIT_COOKIES = [
4729
+ PACK_COOKIE_ID,
4730
+ PACK_TEST_COOKIE_ID,
4731
+ "_ga",
4732
+ "_shopify_y"
4733
+ ];
4734
+ function sessionVerdict(incoming) {
4735
+ const stored = incoming[PACK_SESSION_COOKIE_ID];
4736
+ if (stored === "r" || stored === "n") return stored;
4737
+ return PRIOR_VISIT_COOKIES.some((name) => incoming[name]) ? "r" : "n";
4738
+ }
4739
+ function sessionMarkerSetCookie(verdict, omitSecure) {
4740
+ return `${PACK_SESSION_COOKIE_ID}=${verdict}; Path=/; Max-Age=${PACK_SESSION_MAX_AGE}; SameSite=None${omitSecure ? "" : "; Secure"}`;
4741
+ }
4742
+
4743
+ // src/handle-request.ts
4744
+ async function handleRequest(pack, request, handleRequest2) {
4745
+ const packHandleResponse = await pack.handleRequest(request);
4746
+ let response;
4747
+ response = await packlytics(pack, request, () => {
4748
+ return handleRequest2(request);
4749
+ });
4750
+ packHandleResponse(response);
4751
+ response.headers.append("powered-by", "Shopify, Hydrogen + Pack Digital");
4752
+ response.headers.append("Set-Cookie", await pack.session.commit());
4753
+ const incoming = import_cookie2.default.parse(request.headers.get("Cookie") || "");
4754
+ const omitSecure = isSafari(request.headers.get("User-Agent")) && process.env.NODE_ENV === "development";
4755
+ response.headers.append(
4756
+ "Set-Cookie",
4757
+ sessionMarkerSetCookie(sessionVerdict(incoming), omitSecure)
4758
+ );
4759
+ if (pack.testSession && pack.testSession.hasChanges()) {
4760
+ response.headers.append("Set-Cookie", await pack.testSession.commit());
4761
+ }
4762
+ return response;
4763
+ }
4764
+
4765
+ // src/session/usePackCookies.ts
4766
+ var import_cookie3 = __toESM(require_cookie(), 1);
4767
+ import { useEffect } from "react";
4768
+ function usePackCookies(options) {
4769
+ const { hasUserConsent: hasUserConsent2 = true, domain = "" } = options || {};
4770
+ useEffect(() => {
4771
+ setCookie(
4772
+ PACK_USER_CONSENT_COOKIE_ID,
4773
+ hasUserConsent2 ? "true" : "false",
4774
+ PACK_COOKIE_MAX_AGE,
4775
+ domain
4776
+ );
4777
+ }, [hasUserConsent2]);
4778
+ }
4779
+ function setCookie(name, value, maxage, domain) {
4780
+ document.cookie = (0, import_cookie3.serialize)(name, value, {
4781
+ maxAge: maxage,
4782
+ domain,
4783
+ sameSite: "lax",
4784
+ path: "/"
4785
+ });
4769
4786
  }
4787
+
4788
+ // src/session/session.ts
4789
+ import {
4790
+ createCookie,
4791
+ createCookieSessionStorage
4792
+ } from "react-router";
4770
4793
  var PackSession = class {
4771
4794
  id;
4772
4795
  secret;
@@ -4823,10 +4846,6 @@ import {
4823
4846
  } from "react-router";
4824
4847
  var import_debug = __toESM(require_browser(), 1);
4825
4848
  var debug = (0, import_debug.default)("pack:ab-testing:test-session");
4826
- function isSafari2(userAgent) {
4827
- if (!userAgent) return false;
4828
- return /Safari\//.test(userAgent) && !/Chrome\//.test(userAgent) && !/Chromium\//.test(userAgent) && !/Edg\//.test(userAgent) && !/Firefox\//.test(userAgent);
4829
- }
4830
4849
  var PackTestSession = class {
4831
4850
  id;
4832
4851
  #session;
@@ -4852,7 +4871,7 @@ var PackTestSession = class {
4852
4871
  });
4853
4872
  const storage = createCookieSessionStorage2({
4854
4873
  cookie: createCookie2(PACK_TEST_COOKIE_ID, {
4855
- secure: isSafari2(userAgent) && process.env.NODE_ENV === "development" ? false : true,
4874
+ secure: isSafari(userAgent) && process.env.NODE_ENV === "development" ? false : true,
4856
4875
  secrets,
4857
4876
  sameSite: "lax",
4858
4877
  maxAge: PACK_COOKIE_MAX_AGE,
@@ -5017,12 +5036,12 @@ var PackTestSession = class {
5017
5036
  };
5018
5037
 
5019
5038
  // src/create-pack-client.ts
5020
- var cookie3 = __toESM(require_cookie(), 1);
5039
+ var cookie5 = __toESM(require_cookie(), 1);
5021
5040
  import { PackClient } from "@pack/client";
5022
5041
  import { CacheCustom, createWithCache } from "@shopify/hydrogen";
5023
5042
 
5024
5043
  // src/tests/test.ts
5025
- var cookie2 = __toESM(require_cookie(), 1);
5044
+ var cookie4 = __toESM(require_cookie(), 1);
5026
5045
 
5027
5046
  // src/tests/local-test-resolver.ts
5028
5047
  var import_json_rules_engine = __toESM(require_dist(), 1);
@@ -5045,6 +5064,379 @@ function getImpressionSectionIdsForVariant(test, testVariant) {
5045
5064
  return [...new Set(fallbackSectionIds)];
5046
5065
  }
5047
5066
 
5067
+ // src/audiences/identity-bundle.ts
5068
+ var cookie3 = __toESM(require_cookie(), 1);
5069
+ function detectDeviceType(userAgent) {
5070
+ const ua = userAgent.toLowerCase();
5071
+ if (/ipad|tablet|(android(?!.*mobile))/.test(ua)) return "tablet";
5072
+ if (/mobi|iphone|android|ipod|phone/.test(ua)) return "mobile";
5073
+ return "desktop";
5074
+ }
5075
+ function extractGa4ClientId(cookies) {
5076
+ const ga = cookies["_ga"];
5077
+ if (!ga) return void 0;
5078
+ const parts = ga.split(".");
5079
+ return parts.length >= 4 ? parts.slice(-2).join(".") : void 0;
5080
+ }
5081
+ function firstHeader(request, ...names) {
5082
+ for (const name of names) {
5083
+ const value = request.headers.get(name);
5084
+ if (value) return value;
5085
+ }
5086
+ return void 0;
5087
+ }
5088
+ function buildIdentityBundle(request, options = {}) {
5089
+ const parsedCookies = cookie3.parse(request.headers.get("cookie") || "");
5090
+ const cookies = {};
5091
+ for (const [name, value] of Object.entries(parsedCookies)) {
5092
+ if (typeof value === "string") cookies[name] = value;
5093
+ }
5094
+ const userAgent = request.headers.get("user-agent") || void 0;
5095
+ const consentCookie = cookies[PACK_USER_CONSENT_COOKIE_ID];
5096
+ const hasConsent = consentCookie ? consentCookie === "true" : true;
5097
+ const requestUrl = new URL(request.url);
5098
+ const query = {};
5099
+ requestUrl.searchParams.forEach((value, key) => {
5100
+ query[key] = value;
5101
+ });
5102
+ return {
5103
+ shopifyCustomerId: options.shopifyCustomerId,
5104
+ // Only GA4 is populated today; other provider ids are filled in by the
5105
+ // condition tasks that own them (see the type declaration).
5106
+ ga4ClientId: extractGa4ClientId(cookies),
5107
+ cookies,
5108
+ hasConsent,
5109
+ userAgent,
5110
+ deviceType: userAgent ? detectDeviceType(userAgent) : "desktop",
5111
+ // Host-provided geo-IP, first header present wins. Every name here is one
5112
+ // the host defines, and they must carry the same FORMAT across hosts:
5113
+ // country as ISO 3166-1 alpha-2, region as an ISO 3166-2 subdivision code.
5114
+ //
5115
+ // Defined is not the same as delivered. Only `oxygen-buyer-*` and
5116
+ // `cf-ipcountry` arrive unconditionally; `cf-region-code` needs
5117
+ // Cloudflare's "Add visitor location headers" managed transform, which is
5118
+ // off by default, so a Workers deployment behind plain Cloudflare has no
5119
+ // region and its region audiences match nobody. That is fine while
5120
+ // storefronts are on Oxygen (which sets it from request.cf) — revisit if a
5121
+ // storefront ever fronts an origin with Cloudflare directly.
5122
+ // Oxygen also exposes `oxygen-buyer-region`, but that is the full name
5123
+ // ("California") — mixing it in would make an audience configured as "CA"
5124
+ // match on Cloudflare/Vercel and never on Oxygen, since matchGeo compares
5125
+ // the whole string.
5126
+ geo: {
5127
+ country: firstHeader(
5128
+ request,
5129
+ "oxygen-buyer-country",
5130
+ "cf-ipcountry",
5131
+ "x-vercel-ip-country"
5132
+ ),
5133
+ region: firstHeader(
5134
+ request,
5135
+ "oxygen-buyer-region-code",
5136
+ "cf-region-code",
5137
+ "x-vercel-ip-country-region"
5138
+ )
5139
+ },
5140
+ url: {
5141
+ path: requestUrl.pathname,
5142
+ query,
5143
+ // Public host — prefer the forwarded host over the received one so
5144
+ // self-referral detection works behind a proxy / preview domain, where
5145
+ // request.url carries the internal host (e.g. *.vercel.app) while the
5146
+ // browser typed the storefront domain. x-forwarded-host may be a comma
5147
+ // list and may carry a port.
5148
+ host: (firstHeader(request, "x-forwarded-host", "host") ?? requestUrl.hostname).split(",")[0].trim().split(":")[0].toLowerCase()
5149
+ },
5150
+ referrer: request.headers.get("referer") || void 0
5151
+ };
5152
+ }
5153
+
5154
+ // src/audiences/safe-regex.ts
5155
+ var MAX_REGEX_PATTERN_LENGTH = 200;
5156
+ var MAX_REGEX_INPUT_LENGTH = 4096;
5157
+ function safeRegexMatch(pattern, input) {
5158
+ if (!pattern || input === void 0) return false;
5159
+ if (pattern.length > MAX_REGEX_PATTERN_LENGTH || input.length > MAX_REGEX_INPUT_LENGTH) {
5160
+ return false;
5161
+ }
5162
+ try {
5163
+ return new RegExp(pattern).test(input);
5164
+ } catch {
5165
+ return false;
5166
+ }
5167
+ }
5168
+
5169
+ // src/audiences/conditions/cookie.ts
5170
+ function evaluateCookie(config, identity) {
5171
+ const name = config?.name;
5172
+ const op = config?.op;
5173
+ if (!name || !op) return false;
5174
+ if (config?.requireConsent && !identity.hasConsent) return false;
5175
+ const value = identity.cookies?.[name];
5176
+ const target = config?.value ?? "";
5177
+ switch (op) {
5178
+ case "exists":
5179
+ return value !== void 0 && value !== "";
5180
+ case "equals":
5181
+ return value === target;
5182
+ case "contains":
5183
+ return value !== void 0 && value.includes(target);
5184
+ case "starts_with":
5185
+ return value !== void 0 && value.startsWith(target);
5186
+ case "ends_with":
5187
+ return value !== void 0 && value.endsWith(target);
5188
+ case "matches_regex":
5189
+ return safeRegexMatch(target, value);
5190
+ default:
5191
+ return false;
5192
+ }
5193
+ }
5194
+
5195
+ // src/audiences/conditions/builtin.ts
5196
+ var PAID_MEDIUM = /^(.*cp.*|ppc|retargeting|paid.*)$/;
5197
+ var DISPLAY_MEDIUM = ["display", "banner", "expandable", "interstitial", "cpm"];
5198
+ var SOCIAL_MEDIUM = ["social", "social-network", "social-media", "sm", "social network", "social media"];
5199
+ var ORGANIC_VIDEO_MEDIUM = /^.*video.*$/;
5200
+ var EMAIL_MEDIUM = /^(email|e-mail|e_mail|e mail)$/;
5201
+ var REFERRAL_MEDIUM = ["referral", "app", "link"];
5202
+ var SEARCH_SOURCES = [
5203
+ "google",
5204
+ "bing",
5205
+ "yahoo",
5206
+ "duckduckgo",
5207
+ "baidu",
5208
+ "yandex",
5209
+ "ecosia",
5210
+ "ask.",
5211
+ "aol.",
5212
+ "naver",
5213
+ "daum",
5214
+ "brave",
5215
+ "startpage",
5216
+ "qwant",
5217
+ "sogou",
5218
+ "seznam"
5219
+ ];
5220
+ var SOCIAL_SOURCES = [
5221
+ "facebook",
5222
+ "fb.",
5223
+ "instagram",
5224
+ "tiktok",
5225
+ "pinterest",
5226
+ "pin.it",
5227
+ "linkedin",
5228
+ "lnkd.in",
5229
+ "reddit",
5230
+ "snapchat",
5231
+ "twitter",
5232
+ "x.com",
5233
+ "t.co",
5234
+ "tumblr",
5235
+ "threads",
5236
+ "whatsapp",
5237
+ "wechat",
5238
+ "weibo",
5239
+ "vk.",
5240
+ "quora"
5241
+ ];
5242
+ function hostFromReferrer(referrer) {
5243
+ if (!referrer) return "";
5244
+ try {
5245
+ return new URL(referrer).hostname.toLowerCase();
5246
+ } catch {
5247
+ return "";
5248
+ }
5249
+ }
5250
+ var parseList = (value) => (value ?? "").split(",").map((s) => s.trim()).filter(Boolean);
5251
+ function matchGeo(code, op, value) {
5252
+ if (!code) return false;
5253
+ const c = code.toUpperCase();
5254
+ const list = parseList(value).map((s) => s.toUpperCase());
5255
+ switch (op) {
5256
+ case "equals":
5257
+ return c === (value ?? "").trim().toUpperCase();
5258
+ case "in_list":
5259
+ return list.includes(c);
5260
+ case "not_in_list":
5261
+ return list.length > 0 && !list.includes(c);
5262
+ default:
5263
+ return false;
5264
+ }
5265
+ }
5266
+ function classifyReferrerSource(query, referrer, currentHost) {
5267
+ const medium = (query?.["utm_medium"] ?? "").toLowerCase();
5268
+ const source = (query?.["utm_source"] ?? "").toLowerCase();
5269
+ const stripWww = (h) => h.replace(/^www\./, "");
5270
+ let host = hostFromReferrer(referrer);
5271
+ if (host && currentHost) {
5272
+ const a = stripWww(host);
5273
+ const b = stripWww(currentHost.toLowerCase());
5274
+ if (a === b || a.endsWith("." + b) || b.endsWith("." + a)) host = "";
5275
+ }
5276
+ const bareHost = stripWww(host);
5277
+ const bareKey = (k) => k.endsWith(".") ? k.slice(0, -1) : k;
5278
+ const hostInList = (list) => !!bareHost && list.some((k) => {
5279
+ const key = bareKey(k);
5280
+ return key.includes(".") ? bareHost === key || bareHost.endsWith("." + key) : bareHost.split(".").includes(key);
5281
+ });
5282
+ const sourceInList = (list) => {
5283
+ if (!source) return false;
5284
+ const tokens = source.split(/[.\-_ ]/);
5285
+ return list.some((k) => {
5286
+ const key = bareKey(k);
5287
+ return key.includes(".") ? source === key || source.endsWith("." + key) : tokens.includes(key);
5288
+ });
5289
+ };
5290
+ const inCategory = (list) => sourceInList(list) || hostInList(list);
5291
+ if (PAID_MEDIUM.test(medium) || DISPLAY_MEDIUM.includes(medium)) return "paid";
5292
+ if (SOCIAL_MEDIUM.includes(medium) || inCategory(SOCIAL_SOURCES)) return "social";
5293
+ if (medium === "organic" || ORGANIC_VIDEO_MEDIUM.test(medium) || inCategory(SEARCH_SOURCES)) return "organic";
5294
+ if (EMAIL_MEDIUM.test(medium) || EMAIL_MEDIUM.test(source)) return "email";
5295
+ if (medium === "affiliate" || REFERRAL_MEDIUM.includes(medium) || host || medium || source)
5296
+ return "referral";
5297
+ return "direct";
5298
+ }
5299
+ var BUILTINS = {
5300
+ // Logged in vs. guest — Shopify customer session presence.
5301
+ auth_status: (config, identity) => {
5302
+ const loggedIn = !!identity.shopifyCustomerId;
5303
+ if (config?.value === "logged_in") return loggedIn;
5304
+ if (config?.value === "anonymous") return !loggedIn;
5305
+ return false;
5306
+ },
5307
+ // Device type from the User-Agent; unknown falls back to desktop.
5308
+ device: (config, identity) => (identity.deviceType ?? "desktop") === config?.value,
5309
+ // New vs. returning. The persistent prior-visit cookies say the visitor has
5310
+ // been here at some point, not whether THIS session is their first. The
5311
+ // rolling __pack_s marker carries that verdict ("r"/"n"), decided once at
5312
+ // session start in handle-request (see sessionVerdict in ../visitor-recency).
5313
+ // Mirror that writer's tri-state: "r"/"n" are authoritative; anything else (a
5314
+ // new session with no marker, or a tampered value) falls back to prior visits.
5315
+ // Missing signal → new.
5316
+ //
5317
+ // Consent note: this is the only built-in that reads cookies, and it does NOT
5318
+ // enforce a consent gate. _ga / _shopify_y only exist when analytics consent
5319
+ // was given, but Pack's own __pack / __pack_s are written on every response
5320
+ // regardless of consent (see handle-request), so a visitor who declined can
5321
+ // still be classified from them. Whether that is acceptable is a system-wide
5322
+ // consent-posture decision for the resolver/identity layer — flagged, not
5323
+ // enforced.
5324
+ visitor_recency: (config, identity) => {
5325
+ const cookies = identity.cookies ?? {};
5326
+ const priorVisit = PRIOR_VISIT_COOKIES.some((name) => !!cookies[name]);
5327
+ const session = cookies[PACK_SESSION_COOKIE_ID];
5328
+ const returning = session === "r" ? true : session === "n" ? false : priorVisit;
5329
+ if (config?.value === "returning") return returning;
5330
+ if (config?.value === "new") return !returning;
5331
+ return false;
5332
+ },
5333
+ // Country / region from the CDN geo signal; missing → no match.
5334
+ geo_country: (config, identity) => matchGeo(identity.geo?.country, config?.op, config?.value),
5335
+ geo_region: (config, identity) => matchGeo(identity.geo?.region, config?.op, config?.value),
5336
+ // Traffic source from utm_medium, then Referer host; else direct.
5337
+ referrer_source: (config, identity) => classifyReferrerSource(
5338
+ identity.url?.query,
5339
+ identity.referrer,
5340
+ identity.url?.host
5341
+ ) === config?.value,
5342
+ // A specific UTM parameter on the current URL.
5343
+ utm_param: (config, identity) => {
5344
+ if (!config?.param) return false;
5345
+ const name = config.param.replace(/^utm_/, "");
5346
+ const v = identity.url?.query?.[`utm_${name}`]?.toLowerCase();
5347
+ switch (config?.op) {
5348
+ case "exists":
5349
+ return v !== void 0 && v !== "";
5350
+ case "equals":
5351
+ return v !== void 0 && v === config?.value?.toLowerCase();
5352
+ case "in_list":
5353
+ return v !== void 0 && parseList(config?.value).map((s) => s.toLowerCase()).includes(v);
5354
+ default:
5355
+ return false;
5356
+ }
5357
+ }
5358
+ };
5359
+ function evaluateBuiltin(config, identity) {
5360
+ const evaluator = config?.key ? BUILTINS[config.key] : void 0;
5361
+ return evaluator ? evaluator(config ?? {}, identity) : false;
5362
+ }
5363
+
5364
+ // src/audiences/condition-registry.ts
5365
+ var evaluators = /* @__PURE__ */ new Map();
5366
+ function registerConditionEvaluator(type, evaluate) {
5367
+ if (evaluators.has(type)) {
5368
+ throw new Error(`Condition evaluator "${type}" is already registered.`);
5369
+ }
5370
+ evaluators.set(type, evaluate);
5371
+ }
5372
+ function getConditionEvaluator(type) {
5373
+ return evaluators.get(type);
5374
+ }
5375
+ function isConditionRegistered(type) {
5376
+ return evaluators.has(type);
5377
+ }
5378
+ registerConditionEvaluator("cookie", evaluateCookie);
5379
+ registerConditionEvaluator("builtin", evaluateBuiltin);
5380
+
5381
+ // src/audiences/audience-resolver.ts
5382
+ function isEmptyRules(rules) {
5383
+ return rules == null || typeof rules === "object" && !Array.isArray(rules) && Object.keys(rules).length === 0;
5384
+ }
5385
+ function isGroupNode(node) {
5386
+ return !!node && typeof node === "object" && typeof node.op === "string";
5387
+ }
5388
+ async function resolveRulesTree(rules, identity) {
5389
+ const matchedConditions = [];
5390
+ if (isEmptyRules(rules)) {
5391
+ return { matches: true, matchedConditions };
5392
+ }
5393
+ const matches = await evaluateNode(rules, identity, matchedConditions);
5394
+ return { matches, matchedConditions: matches ? matchedConditions : [] };
5395
+ }
5396
+ async function evaluateNode(node, identity, matched) {
5397
+ if (isGroupNode(node)) {
5398
+ const { op, children } = node;
5399
+ if (op === "AND") {
5400
+ for (const child of children) {
5401
+ if (!await evaluateNode(child, identity, matched)) return false;
5402
+ }
5403
+ return true;
5404
+ }
5405
+ if (op === "OR") {
5406
+ for (const child of children) {
5407
+ if (await evaluateNode(child, identity, matched)) return true;
5408
+ }
5409
+ return false;
5410
+ }
5411
+ if (!children?.length) return false;
5412
+ for (const child of children) {
5413
+ if (await evaluateNode(child, identity, matched)) return false;
5414
+ }
5415
+ return true;
5416
+ }
5417
+ return evaluateLeaf(node, identity, matched);
5418
+ }
5419
+ async function evaluateLeaf(leaf, identity, matched) {
5420
+ const evaluate = getConditionEvaluator(leaf.type);
5421
+ if (!evaluate) return false;
5422
+ let result;
5423
+ try {
5424
+ result = await evaluate(leaf.config ?? {}, identity);
5425
+ } catch {
5426
+ result = false;
5427
+ }
5428
+ if (leaf.negate) result = !result;
5429
+ if (result) {
5430
+ matched.push({
5431
+ type: leaf.type,
5432
+ provider: leaf.provider ?? null,
5433
+ config: leaf.config ?? null,
5434
+ negate: leaf.negate ?? null
5435
+ });
5436
+ }
5437
+ return result;
5438
+ }
5439
+
5048
5440
  // src/tests/local-test-resolver.ts
5049
5441
  var debug2 = (0, import_debug2.default)("pack:ab-testing:local-resolver");
5050
5442
  var LocalTestResolver = class {
@@ -5084,7 +5476,7 @@ var LocalTestResolver = class {
5084
5476
  /**
5085
5477
  * Evaluate test rules and assign a variant
5086
5478
  */
5087
- async assignTest(attributes, _sessionId) {
5479
+ async assignTest(attributes, _sessionId, identity) {
5088
5480
  debug2(
5089
5481
  "[Pack Test LocalResolver] Starting test assignment:",
5090
5482
  JSON.stringify({
@@ -5096,7 +5488,11 @@ var LocalTestResolver = class {
5096
5488
  debug2("[Pack Test LocalResolver] No test rules available");
5097
5489
  return null;
5098
5490
  }
5099
- const eligibleTests = await this.evaluateRules(this.testRules, attributes);
5491
+ const eligibleTests = await this.evaluateRules(
5492
+ this.testRules,
5493
+ attributes,
5494
+ identity
5495
+ );
5100
5496
  debug2(
5101
5497
  "[Pack Test LocalResolver] Rule evaluation result:",
5102
5498
  JSON.stringify({
@@ -5177,7 +5573,7 @@ var LocalTestResolver = class {
5177
5573
  /**
5178
5574
  * Evaluate rules using json-rules-engine (same as tests-service)
5179
5575
  */
5180
- async evaluateRules(tests, attributes) {
5576
+ async evaluateRules(tests, attributes, identity) {
5181
5577
  debug2(
5182
5578
  "[Pack Test LocalResolver] Starting rule evaluation:",
5183
5579
  JSON.stringify({
@@ -5298,7 +5694,7 @@ var LocalTestResolver = class {
5298
5694
  return hasTargetedEvent;
5299
5695
  });
5300
5696
  debug2(
5301
- "[Pack Test LocalResolver] Final eligible tests:",
5697
+ "[Pack Test LocalResolver] Inline-eligible tests:",
5302
5698
  JSON.stringify({
5303
5699
  eligibleCount: eligibleTests.length,
5304
5700
  eligibleTests: eligibleTests.map((t) => ({
@@ -5307,7 +5703,47 @@ var LocalTestResolver = class {
5307
5703
  }))
5308
5704
  })
5309
5705
  );
5310
- return eligibleTests;
5706
+ return this.applyAudienceGating(eligibleTests, identity);
5707
+ }
5708
+ /**
5709
+ * Audience gating (XOR with inline rules): a test attached to an audience is
5710
+ * only eligible when the visitor matches the pinned audience snapshot. Tests
5711
+ * without an audience pass through unchanged. Fails closed (excludes the test)
5712
+ * when the audience can't be evaluated — no identity, or an evaluator error —
5713
+ * so audience-targeted tests never leak to unintended traffic.
5714
+ */
5715
+ async applyAudienceGating(eligibleTests, identity) {
5716
+ const hasAudience = eligibleTests.some(
5717
+ (test) => test.experimentAudience?.pinnedRules
5718
+ );
5719
+ if (!hasAudience) return eligibleTests;
5720
+ const gated = await Promise.all(
5721
+ eligibleTests.map(async (test) => {
5722
+ const pinnedRules = test.experimentAudience?.pinnedRules;
5723
+ if (!pinnedRules) return test;
5724
+ if (!identity) {
5725
+ debug2(
5726
+ `[Pack Test LocalResolver] Test ${test.handle} has an audience but no identity \u2014 excluding`
5727
+ );
5728
+ return null;
5729
+ }
5730
+ try {
5731
+ const { matches } = await resolveRulesTree(pinnedRules, identity);
5732
+ debug2(
5733
+ `[Pack Test LocalResolver] Test ${test.handle} audience match:`,
5734
+ JSON.stringify({ matches })
5735
+ );
5736
+ return matches ? test : null;
5737
+ } catch (err) {
5738
+ debug2(
5739
+ `[Pack Test LocalResolver] Test ${test.handle} audience eval error \u2014 excluding:`,
5740
+ String(err)
5741
+ );
5742
+ return null;
5743
+ }
5744
+ })
5745
+ );
5746
+ return gated.filter((test) => test !== null);
5311
5747
  }
5312
5748
  /**
5313
5749
  * Resolve test and variant IDs from their handles
@@ -5350,6 +5786,10 @@ var QUERY_TESTS_BY_RULES = `#graphql
5350
5786
  value
5351
5787
  }
5352
5788
  impressionTrigger
5789
+ experimentAudience {
5790
+ pinnedVersion
5791
+ pinnedRules
5792
+ }
5353
5793
  testVariants {
5354
5794
  id
5355
5795
  handle
@@ -5688,9 +6128,11 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
5688
6128
  }
5689
6129
  const sessionId = packClient.storeId;
5690
6130
  debug3("[Pack Test] Assigning test with session ID:", sessionId);
6131
+ const identity = buildIdentityBundle(request);
5691
6132
  const assignedTest = await localTestResolver.assignTest(
5692
6133
  testTargetAudienceAttributes || {},
5693
- sessionId
6134
+ sessionId,
6135
+ identity
5694
6136
  );
5695
6137
  debug3(
5696
6138
  "[Pack Test] Test assignment result:",
@@ -5781,7 +6223,7 @@ async function getTestInfo({
5781
6223
  isExpired: testSessionExpireAt ? new Date(testSessionExpireAt) < /* @__PURE__ */ new Date() : false
5782
6224
  })
5783
6225
  );
5784
- const exposedTestCookieString = cookie2.parse(
6226
+ const exposedTestCookieString = cookie4.parse(
5785
6227
  request.headers.get("cookie") || ""
5786
6228
  )?.exposedTest;
5787
6229
  debug3(
@@ -6232,7 +6674,7 @@ function createPackClient(options) {
6232
6674
  if (hasExposedTestCookie) {
6233
6675
  response.headers.append(
6234
6676
  "Set-Cookie",
6235
- cookie3.serialize("exposedTest", "", {
6677
+ cookie5.serialize("exposedTest", "", {
6236
6678
  maxAge: 0,
6237
6679
  expires: /* @__PURE__ */ new Date(0),
6238
6680
  path: "/"
@@ -6994,10 +7436,15 @@ export {
6994
7436
  PackTestProvider,
6995
7437
  PackTestRoute,
6996
7438
  PackTestSession,
7439
+ buildIdentityBundle,
6997
7440
  createPackClient,
7441
+ getConditionEvaluator,
6998
7442
  handleRequest,
7443
+ isConditionRegistered,
6999
7444
  action as previewModeAction,
7000
7445
  loader as previewModeLoader,
7446
+ registerConditionEvaluator,
7447
+ resolveRulesTree,
7001
7448
  useAbTest,
7002
7449
  useAbTestHandle,
7003
7450
  useAbTestId,