@pack/hydrogen 3.2.7 → 3.3.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/index.d.ts +85 -1
- package/dist/index.js +698 -263
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
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
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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/
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
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:
|
|
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
|
|
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
|
|
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,364 @@ 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
|
+
geo: {
|
|
5112
|
+
country: firstHeader(
|
|
5113
|
+
request,
|
|
5114
|
+
"oxygen-buyer-country",
|
|
5115
|
+
"cf-ipcountry",
|
|
5116
|
+
"x-vercel-ip-country"
|
|
5117
|
+
),
|
|
5118
|
+
region: firstHeader(
|
|
5119
|
+
request,
|
|
5120
|
+
"oxygen-buyer-province",
|
|
5121
|
+
"cf-region-code",
|
|
5122
|
+
"x-vercel-ip-country-region"
|
|
5123
|
+
)
|
|
5124
|
+
},
|
|
5125
|
+
url: {
|
|
5126
|
+
path: requestUrl.pathname,
|
|
5127
|
+
query,
|
|
5128
|
+
// Public host — prefer the forwarded host over the received one so
|
|
5129
|
+
// self-referral detection works behind a proxy / preview domain, where
|
|
5130
|
+
// request.url carries the internal host (e.g. *.vercel.app) while the
|
|
5131
|
+
// browser typed the storefront domain. x-forwarded-host may be a comma
|
|
5132
|
+
// list and may carry a port.
|
|
5133
|
+
host: (firstHeader(request, "x-forwarded-host", "host") ?? requestUrl.hostname).split(",")[0].trim().split(":")[0].toLowerCase()
|
|
5134
|
+
},
|
|
5135
|
+
referrer: request.headers.get("referer") || void 0
|
|
5136
|
+
};
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5139
|
+
// src/audiences/safe-regex.ts
|
|
5140
|
+
var MAX_REGEX_PATTERN_LENGTH = 200;
|
|
5141
|
+
var MAX_REGEX_INPUT_LENGTH = 4096;
|
|
5142
|
+
function safeRegexMatch(pattern, input) {
|
|
5143
|
+
if (!pattern || input === void 0) return false;
|
|
5144
|
+
if (pattern.length > MAX_REGEX_PATTERN_LENGTH || input.length > MAX_REGEX_INPUT_LENGTH) {
|
|
5145
|
+
return false;
|
|
5146
|
+
}
|
|
5147
|
+
try {
|
|
5148
|
+
return new RegExp(pattern).test(input);
|
|
5149
|
+
} catch {
|
|
5150
|
+
return false;
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5153
|
+
|
|
5154
|
+
// src/audiences/conditions/cookie.ts
|
|
5155
|
+
function evaluateCookie(config, identity) {
|
|
5156
|
+
const name = config?.name;
|
|
5157
|
+
const op = config?.op;
|
|
5158
|
+
if (!name || !op) return false;
|
|
5159
|
+
if (config?.requireConsent && !identity.hasConsent) return false;
|
|
5160
|
+
const value = identity.cookies?.[name];
|
|
5161
|
+
const target = config?.value ?? "";
|
|
5162
|
+
switch (op) {
|
|
5163
|
+
case "exists":
|
|
5164
|
+
return value !== void 0 && value !== "";
|
|
5165
|
+
case "equals":
|
|
5166
|
+
return value === target;
|
|
5167
|
+
case "contains":
|
|
5168
|
+
return value !== void 0 && value.includes(target);
|
|
5169
|
+
case "starts_with":
|
|
5170
|
+
return value !== void 0 && value.startsWith(target);
|
|
5171
|
+
case "ends_with":
|
|
5172
|
+
return value !== void 0 && value.endsWith(target);
|
|
5173
|
+
case "matches_regex":
|
|
5174
|
+
return safeRegexMatch(target, value);
|
|
5175
|
+
default:
|
|
5176
|
+
return false;
|
|
5177
|
+
}
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5180
|
+
// src/audiences/conditions/builtin.ts
|
|
5181
|
+
var PAID_MEDIUM = /^(.*cp.*|ppc|retargeting|paid.*)$/;
|
|
5182
|
+
var DISPLAY_MEDIUM = ["display", "banner", "expandable", "interstitial", "cpm"];
|
|
5183
|
+
var SOCIAL_MEDIUM = ["social", "social-network", "social-media", "sm", "social network", "social media"];
|
|
5184
|
+
var ORGANIC_VIDEO_MEDIUM = /^.*video.*$/;
|
|
5185
|
+
var EMAIL_MEDIUM = /^(email|e-mail|e_mail|e mail)$/;
|
|
5186
|
+
var REFERRAL_MEDIUM = ["referral", "app", "link"];
|
|
5187
|
+
var SEARCH_SOURCES = [
|
|
5188
|
+
"google",
|
|
5189
|
+
"bing",
|
|
5190
|
+
"yahoo",
|
|
5191
|
+
"duckduckgo",
|
|
5192
|
+
"baidu",
|
|
5193
|
+
"yandex",
|
|
5194
|
+
"ecosia",
|
|
5195
|
+
"ask.",
|
|
5196
|
+
"aol.",
|
|
5197
|
+
"naver",
|
|
5198
|
+
"daum",
|
|
5199
|
+
"brave",
|
|
5200
|
+
"startpage",
|
|
5201
|
+
"qwant",
|
|
5202
|
+
"sogou",
|
|
5203
|
+
"seznam"
|
|
5204
|
+
];
|
|
5205
|
+
var SOCIAL_SOURCES = [
|
|
5206
|
+
"facebook",
|
|
5207
|
+
"fb.",
|
|
5208
|
+
"instagram",
|
|
5209
|
+
"tiktok",
|
|
5210
|
+
"pinterest",
|
|
5211
|
+
"pin.it",
|
|
5212
|
+
"linkedin",
|
|
5213
|
+
"lnkd.in",
|
|
5214
|
+
"reddit",
|
|
5215
|
+
"snapchat",
|
|
5216
|
+
"twitter",
|
|
5217
|
+
"x.com",
|
|
5218
|
+
"t.co",
|
|
5219
|
+
"tumblr",
|
|
5220
|
+
"threads",
|
|
5221
|
+
"whatsapp",
|
|
5222
|
+
"wechat",
|
|
5223
|
+
"weibo",
|
|
5224
|
+
"vk.",
|
|
5225
|
+
"quora"
|
|
5226
|
+
];
|
|
5227
|
+
function hostFromReferrer(referrer) {
|
|
5228
|
+
if (!referrer) return "";
|
|
5229
|
+
try {
|
|
5230
|
+
return new URL(referrer).hostname.toLowerCase();
|
|
5231
|
+
} catch {
|
|
5232
|
+
return "";
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
var parseList = (value) => (value ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
5236
|
+
function matchGeo(code, op, value) {
|
|
5237
|
+
if (!code) return false;
|
|
5238
|
+
const c = code.toUpperCase();
|
|
5239
|
+
const list = parseList(value).map((s) => s.toUpperCase());
|
|
5240
|
+
switch (op) {
|
|
5241
|
+
case "equals":
|
|
5242
|
+
return c === (value ?? "").trim().toUpperCase();
|
|
5243
|
+
case "in_list":
|
|
5244
|
+
return list.includes(c);
|
|
5245
|
+
case "not_in_list":
|
|
5246
|
+
return list.length > 0 && !list.includes(c);
|
|
5247
|
+
default:
|
|
5248
|
+
return false;
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5251
|
+
function classifyReferrerSource(query, referrer, currentHost) {
|
|
5252
|
+
const medium = (query?.["utm_medium"] ?? "").toLowerCase();
|
|
5253
|
+
const source = (query?.["utm_source"] ?? "").toLowerCase();
|
|
5254
|
+
const stripWww = (h) => h.replace(/^www\./, "");
|
|
5255
|
+
let host = hostFromReferrer(referrer);
|
|
5256
|
+
if (host && currentHost) {
|
|
5257
|
+
const a = stripWww(host);
|
|
5258
|
+
const b = stripWww(currentHost.toLowerCase());
|
|
5259
|
+
if (a === b || a.endsWith("." + b) || b.endsWith("." + a)) host = "";
|
|
5260
|
+
}
|
|
5261
|
+
const bareHost = stripWww(host);
|
|
5262
|
+
const bareKey = (k) => k.endsWith(".") ? k.slice(0, -1) : k;
|
|
5263
|
+
const hostInList = (list) => !!bareHost && list.some((k) => {
|
|
5264
|
+
const key = bareKey(k);
|
|
5265
|
+
return key.includes(".") ? bareHost === key || bareHost.endsWith("." + key) : bareHost.split(".").includes(key);
|
|
5266
|
+
});
|
|
5267
|
+
const sourceInList = (list) => {
|
|
5268
|
+
if (!source) return false;
|
|
5269
|
+
const tokens = source.split(/[.\-_ ]/);
|
|
5270
|
+
return list.some((k) => {
|
|
5271
|
+
const key = bareKey(k);
|
|
5272
|
+
return key.includes(".") ? source === key || source.endsWith("." + key) : tokens.includes(key);
|
|
5273
|
+
});
|
|
5274
|
+
};
|
|
5275
|
+
const inCategory = (list) => sourceInList(list) || hostInList(list);
|
|
5276
|
+
if (PAID_MEDIUM.test(medium) || DISPLAY_MEDIUM.includes(medium)) return "paid";
|
|
5277
|
+
if (SOCIAL_MEDIUM.includes(medium) || inCategory(SOCIAL_SOURCES)) return "social";
|
|
5278
|
+
if (medium === "organic" || ORGANIC_VIDEO_MEDIUM.test(medium) || inCategory(SEARCH_SOURCES)) return "organic";
|
|
5279
|
+
if (EMAIL_MEDIUM.test(medium) || EMAIL_MEDIUM.test(source)) return "email";
|
|
5280
|
+
if (medium === "affiliate" || REFERRAL_MEDIUM.includes(medium) || host || medium || source)
|
|
5281
|
+
return "referral";
|
|
5282
|
+
return "direct";
|
|
5283
|
+
}
|
|
5284
|
+
var BUILTINS = {
|
|
5285
|
+
// Logged in vs. guest — Shopify customer session presence.
|
|
5286
|
+
auth_status: (config, identity) => {
|
|
5287
|
+
const loggedIn = !!identity.shopifyCustomerId;
|
|
5288
|
+
if (config?.value === "logged_in") return loggedIn;
|
|
5289
|
+
if (config?.value === "anonymous") return !loggedIn;
|
|
5290
|
+
return false;
|
|
5291
|
+
},
|
|
5292
|
+
// Device type from the User-Agent; unknown falls back to desktop.
|
|
5293
|
+
device: (config, identity) => (identity.deviceType ?? "desktop") === config?.value,
|
|
5294
|
+
// New vs. returning. The persistent prior-visit cookies say the visitor has
|
|
5295
|
+
// been here at some point, not whether THIS session is their first. The
|
|
5296
|
+
// rolling __pack_s marker carries that verdict ("r"/"n"), decided once at
|
|
5297
|
+
// session start in handle-request (see sessionVerdict in ../visitor-recency).
|
|
5298
|
+
// Mirror that writer's tri-state: "r"/"n" are authoritative; anything else (a
|
|
5299
|
+
// new session with no marker, or a tampered value) falls back to prior visits.
|
|
5300
|
+
// Missing signal → new.
|
|
5301
|
+
//
|
|
5302
|
+
// Consent note: this is the only built-in that reads cookies, and it does NOT
|
|
5303
|
+
// enforce a consent gate. _ga / _shopify_y only exist when analytics consent
|
|
5304
|
+
// was given, but Pack's own __pack / __pack_s are written on every response
|
|
5305
|
+
// regardless of consent (see handle-request), so a visitor who declined can
|
|
5306
|
+
// still be classified from them. Whether that is acceptable is a system-wide
|
|
5307
|
+
// consent-posture decision for the resolver/identity layer — flagged, not
|
|
5308
|
+
// enforced.
|
|
5309
|
+
visitor_recency: (config, identity) => {
|
|
5310
|
+
const cookies = identity.cookies ?? {};
|
|
5311
|
+
const priorVisit = PRIOR_VISIT_COOKIES.some((name) => !!cookies[name]);
|
|
5312
|
+
const session = cookies[PACK_SESSION_COOKIE_ID];
|
|
5313
|
+
const returning = session === "r" ? true : session === "n" ? false : priorVisit;
|
|
5314
|
+
if (config?.value === "returning") return returning;
|
|
5315
|
+
if (config?.value === "new") return !returning;
|
|
5316
|
+
return false;
|
|
5317
|
+
},
|
|
5318
|
+
// Country / region from the CDN geo signal; missing → no match.
|
|
5319
|
+
geo_country: (config, identity) => matchGeo(identity.geo?.country, config?.op, config?.value),
|
|
5320
|
+
geo_region: (config, identity) => matchGeo(identity.geo?.region, config?.op, config?.value),
|
|
5321
|
+
// Traffic source from utm_medium, then Referer host; else direct.
|
|
5322
|
+
referrer_source: (config, identity) => classifyReferrerSource(
|
|
5323
|
+
identity.url?.query,
|
|
5324
|
+
identity.referrer,
|
|
5325
|
+
identity.url?.host
|
|
5326
|
+
) === config?.value,
|
|
5327
|
+
// A specific UTM parameter on the current URL.
|
|
5328
|
+
utm_param: (config, identity) => {
|
|
5329
|
+
if (!config?.param) return false;
|
|
5330
|
+
const name = config.param.replace(/^utm_/, "");
|
|
5331
|
+
const v = identity.url?.query?.[`utm_${name}`]?.toLowerCase();
|
|
5332
|
+
switch (config?.op) {
|
|
5333
|
+
case "exists":
|
|
5334
|
+
return v !== void 0 && v !== "";
|
|
5335
|
+
case "equals":
|
|
5336
|
+
return v !== void 0 && v === config?.value?.toLowerCase();
|
|
5337
|
+
case "in_list":
|
|
5338
|
+
return v !== void 0 && parseList(config?.value).map((s) => s.toLowerCase()).includes(v);
|
|
5339
|
+
default:
|
|
5340
|
+
return false;
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
};
|
|
5344
|
+
function evaluateBuiltin(config, identity) {
|
|
5345
|
+
const evaluator = config?.key ? BUILTINS[config.key] : void 0;
|
|
5346
|
+
return evaluator ? evaluator(config ?? {}, identity) : false;
|
|
5347
|
+
}
|
|
5348
|
+
|
|
5349
|
+
// src/audiences/condition-registry.ts
|
|
5350
|
+
var evaluators = /* @__PURE__ */ new Map();
|
|
5351
|
+
function registerConditionEvaluator(type, evaluate) {
|
|
5352
|
+
if (evaluators.has(type)) {
|
|
5353
|
+
throw new Error(`Condition evaluator "${type}" is already registered.`);
|
|
5354
|
+
}
|
|
5355
|
+
evaluators.set(type, evaluate);
|
|
5356
|
+
}
|
|
5357
|
+
function getConditionEvaluator(type) {
|
|
5358
|
+
return evaluators.get(type);
|
|
5359
|
+
}
|
|
5360
|
+
function isConditionRegistered(type) {
|
|
5361
|
+
return evaluators.has(type);
|
|
5362
|
+
}
|
|
5363
|
+
registerConditionEvaluator("cookie", evaluateCookie);
|
|
5364
|
+
registerConditionEvaluator("builtin", evaluateBuiltin);
|
|
5365
|
+
|
|
5366
|
+
// src/audiences/audience-resolver.ts
|
|
5367
|
+
function isEmptyRules(rules) {
|
|
5368
|
+
return rules == null || typeof rules === "object" && !Array.isArray(rules) && Object.keys(rules).length === 0;
|
|
5369
|
+
}
|
|
5370
|
+
function isGroupNode(node) {
|
|
5371
|
+
return !!node && typeof node === "object" && typeof node.op === "string";
|
|
5372
|
+
}
|
|
5373
|
+
async function resolveRulesTree(rules, identity) {
|
|
5374
|
+
const matchedConditions = [];
|
|
5375
|
+
if (isEmptyRules(rules)) {
|
|
5376
|
+
return { matches: true, matchedConditions };
|
|
5377
|
+
}
|
|
5378
|
+
const matches = await evaluateNode(rules, identity, matchedConditions);
|
|
5379
|
+
return { matches, matchedConditions: matches ? matchedConditions : [] };
|
|
5380
|
+
}
|
|
5381
|
+
async function evaluateNode(node, identity, matched) {
|
|
5382
|
+
if (isGroupNode(node)) {
|
|
5383
|
+
const { op, children } = node;
|
|
5384
|
+
if (op === "AND") {
|
|
5385
|
+
for (const child of children) {
|
|
5386
|
+
if (!await evaluateNode(child, identity, matched)) return false;
|
|
5387
|
+
}
|
|
5388
|
+
return true;
|
|
5389
|
+
}
|
|
5390
|
+
if (op === "OR") {
|
|
5391
|
+
for (const child of children) {
|
|
5392
|
+
if (await evaluateNode(child, identity, matched)) return true;
|
|
5393
|
+
}
|
|
5394
|
+
return false;
|
|
5395
|
+
}
|
|
5396
|
+
if (!children?.length) return false;
|
|
5397
|
+
for (const child of children) {
|
|
5398
|
+
if (await evaluateNode(child, identity, matched)) return false;
|
|
5399
|
+
}
|
|
5400
|
+
return true;
|
|
5401
|
+
}
|
|
5402
|
+
return evaluateLeaf(node, identity, matched);
|
|
5403
|
+
}
|
|
5404
|
+
async function evaluateLeaf(leaf, identity, matched) {
|
|
5405
|
+
const evaluate = getConditionEvaluator(leaf.type);
|
|
5406
|
+
if (!evaluate) return false;
|
|
5407
|
+
let result;
|
|
5408
|
+
try {
|
|
5409
|
+
result = await evaluate(leaf.config ?? {}, identity);
|
|
5410
|
+
} catch {
|
|
5411
|
+
result = false;
|
|
5412
|
+
}
|
|
5413
|
+
if (leaf.negate) result = !result;
|
|
5414
|
+
if (result) {
|
|
5415
|
+
matched.push({
|
|
5416
|
+
type: leaf.type,
|
|
5417
|
+
provider: leaf.provider ?? null,
|
|
5418
|
+
config: leaf.config ?? null,
|
|
5419
|
+
negate: leaf.negate ?? null
|
|
5420
|
+
});
|
|
5421
|
+
}
|
|
5422
|
+
return result;
|
|
5423
|
+
}
|
|
5424
|
+
|
|
5048
5425
|
// src/tests/local-test-resolver.ts
|
|
5049
5426
|
var debug2 = (0, import_debug2.default)("pack:ab-testing:local-resolver");
|
|
5050
5427
|
var LocalTestResolver = class {
|
|
@@ -5084,7 +5461,7 @@ var LocalTestResolver = class {
|
|
|
5084
5461
|
/**
|
|
5085
5462
|
* Evaluate test rules and assign a variant
|
|
5086
5463
|
*/
|
|
5087
|
-
async assignTest(attributes, _sessionId) {
|
|
5464
|
+
async assignTest(attributes, _sessionId, identity) {
|
|
5088
5465
|
debug2(
|
|
5089
5466
|
"[Pack Test LocalResolver] Starting test assignment:",
|
|
5090
5467
|
JSON.stringify({
|
|
@@ -5096,7 +5473,11 @@ var LocalTestResolver = class {
|
|
|
5096
5473
|
debug2("[Pack Test LocalResolver] No test rules available");
|
|
5097
5474
|
return null;
|
|
5098
5475
|
}
|
|
5099
|
-
const eligibleTests = await this.evaluateRules(
|
|
5476
|
+
const eligibleTests = await this.evaluateRules(
|
|
5477
|
+
this.testRules,
|
|
5478
|
+
attributes,
|
|
5479
|
+
identity
|
|
5480
|
+
);
|
|
5100
5481
|
debug2(
|
|
5101
5482
|
"[Pack Test LocalResolver] Rule evaluation result:",
|
|
5102
5483
|
JSON.stringify({
|
|
@@ -5177,7 +5558,7 @@ var LocalTestResolver = class {
|
|
|
5177
5558
|
/**
|
|
5178
5559
|
* Evaluate rules using json-rules-engine (same as tests-service)
|
|
5179
5560
|
*/
|
|
5180
|
-
async evaluateRules(tests, attributes) {
|
|
5561
|
+
async evaluateRules(tests, attributes, identity) {
|
|
5181
5562
|
debug2(
|
|
5182
5563
|
"[Pack Test LocalResolver] Starting rule evaluation:",
|
|
5183
5564
|
JSON.stringify({
|
|
@@ -5298,7 +5679,7 @@ var LocalTestResolver = class {
|
|
|
5298
5679
|
return hasTargetedEvent;
|
|
5299
5680
|
});
|
|
5300
5681
|
debug2(
|
|
5301
|
-
"[Pack Test LocalResolver]
|
|
5682
|
+
"[Pack Test LocalResolver] Inline-eligible tests:",
|
|
5302
5683
|
JSON.stringify({
|
|
5303
5684
|
eligibleCount: eligibleTests.length,
|
|
5304
5685
|
eligibleTests: eligibleTests.map((t) => ({
|
|
@@ -5307,7 +5688,47 @@ var LocalTestResolver = class {
|
|
|
5307
5688
|
}))
|
|
5308
5689
|
})
|
|
5309
5690
|
);
|
|
5310
|
-
return eligibleTests;
|
|
5691
|
+
return this.applyAudienceGating(eligibleTests, identity);
|
|
5692
|
+
}
|
|
5693
|
+
/**
|
|
5694
|
+
* Audience gating (XOR with inline rules): a test attached to an audience is
|
|
5695
|
+
* only eligible when the visitor matches the pinned audience snapshot. Tests
|
|
5696
|
+
* without an audience pass through unchanged. Fails closed (excludes the test)
|
|
5697
|
+
* when the audience can't be evaluated — no identity, or an evaluator error —
|
|
5698
|
+
* so audience-targeted tests never leak to unintended traffic.
|
|
5699
|
+
*/
|
|
5700
|
+
async applyAudienceGating(eligibleTests, identity) {
|
|
5701
|
+
const hasAudience = eligibleTests.some(
|
|
5702
|
+
(test) => test.experimentAudience?.pinnedRules
|
|
5703
|
+
);
|
|
5704
|
+
if (!hasAudience) return eligibleTests;
|
|
5705
|
+
const gated = await Promise.all(
|
|
5706
|
+
eligibleTests.map(async (test) => {
|
|
5707
|
+
const pinnedRules = test.experimentAudience?.pinnedRules;
|
|
5708
|
+
if (!pinnedRules) return test;
|
|
5709
|
+
if (!identity) {
|
|
5710
|
+
debug2(
|
|
5711
|
+
`[Pack Test LocalResolver] Test ${test.handle} has an audience but no identity \u2014 excluding`
|
|
5712
|
+
);
|
|
5713
|
+
return null;
|
|
5714
|
+
}
|
|
5715
|
+
try {
|
|
5716
|
+
const { matches } = await resolveRulesTree(pinnedRules, identity);
|
|
5717
|
+
debug2(
|
|
5718
|
+
`[Pack Test LocalResolver] Test ${test.handle} audience match:`,
|
|
5719
|
+
JSON.stringify({ matches })
|
|
5720
|
+
);
|
|
5721
|
+
return matches ? test : null;
|
|
5722
|
+
} catch (err) {
|
|
5723
|
+
debug2(
|
|
5724
|
+
`[Pack Test LocalResolver] Test ${test.handle} audience eval error \u2014 excluding:`,
|
|
5725
|
+
String(err)
|
|
5726
|
+
);
|
|
5727
|
+
return null;
|
|
5728
|
+
}
|
|
5729
|
+
})
|
|
5730
|
+
);
|
|
5731
|
+
return gated.filter((test) => test !== null);
|
|
5311
5732
|
}
|
|
5312
5733
|
/**
|
|
5313
5734
|
* Resolve test and variant IDs from their handles
|
|
@@ -5350,6 +5771,10 @@ var QUERY_TESTS_BY_RULES = `#graphql
|
|
|
5350
5771
|
value
|
|
5351
5772
|
}
|
|
5352
5773
|
impressionTrigger
|
|
5774
|
+
experimentAudience {
|
|
5775
|
+
pinnedVersion
|
|
5776
|
+
pinnedRules
|
|
5777
|
+
}
|
|
5353
5778
|
testVariants {
|
|
5354
5779
|
id
|
|
5355
5780
|
handle
|
|
@@ -5688,9 +6113,11 @@ async function packClientFetchTestByRules(packClient, testTargetAudienceAttribut
|
|
|
5688
6113
|
}
|
|
5689
6114
|
const sessionId = packClient.storeId;
|
|
5690
6115
|
debug3("[Pack Test] Assigning test with session ID:", sessionId);
|
|
6116
|
+
const identity = buildIdentityBundle(request);
|
|
5691
6117
|
const assignedTest = await localTestResolver.assignTest(
|
|
5692
6118
|
testTargetAudienceAttributes || {},
|
|
5693
|
-
sessionId
|
|
6119
|
+
sessionId,
|
|
6120
|
+
identity
|
|
5694
6121
|
);
|
|
5695
6122
|
debug3(
|
|
5696
6123
|
"[Pack Test] Test assignment result:",
|
|
@@ -5781,7 +6208,7 @@ async function getTestInfo({
|
|
|
5781
6208
|
isExpired: testSessionExpireAt ? new Date(testSessionExpireAt) < /* @__PURE__ */ new Date() : false
|
|
5782
6209
|
})
|
|
5783
6210
|
);
|
|
5784
|
-
const exposedTestCookieString =
|
|
6211
|
+
const exposedTestCookieString = cookie4.parse(
|
|
5785
6212
|
request.headers.get("cookie") || ""
|
|
5786
6213
|
)?.exposedTest;
|
|
5787
6214
|
debug3(
|
|
@@ -6198,6 +6625,7 @@ function createPackClient(options) {
|
|
|
6198
6625
|
const previewEnabled = !!session.get("previewEnabled");
|
|
6199
6626
|
const previewEnvironment = session.get("environment");
|
|
6200
6627
|
const locale = session.get("locale");
|
|
6628
|
+
const releaseId = session.get("releaseId");
|
|
6201
6629
|
const pageDraft = session.get("pageDraft");
|
|
6202
6630
|
const siteSettingsDraft = session.get("siteSettingsDraft");
|
|
6203
6631
|
const testHandle = session.get("testHandle");
|
|
@@ -6231,7 +6659,7 @@ function createPackClient(options) {
|
|
|
6231
6659
|
if (hasExposedTestCookie) {
|
|
6232
6660
|
response.headers.append(
|
|
6233
6661
|
"Set-Cookie",
|
|
6234
|
-
|
|
6662
|
+
cookie5.serialize("exposedTest", "", {
|
|
6235
6663
|
maxAge: 0,
|
|
6236
6664
|
expires: /* @__PURE__ */ new Date(0),
|
|
6237
6665
|
path: "/"
|
|
@@ -6291,6 +6719,7 @@ function createPackClient(options) {
|
|
|
6291
6719
|
sessionId: session.id,
|
|
6292
6720
|
clientName: "HydrogenClient",
|
|
6293
6721
|
locale,
|
|
6722
|
+
releaseId,
|
|
6294
6723
|
pageDraft,
|
|
6295
6724
|
siteSettingsDraft,
|
|
6296
6725
|
testHandle,
|
|
@@ -6523,6 +6952,7 @@ async function loader({ request, context }) {
|
|
|
6523
6952
|
pack.session.set("environment", environment);
|
|
6524
6953
|
pack.session.set("customizerMeta", customizerMetaJson);
|
|
6525
6954
|
pack.session.set("locale", previewContext?.locale ?? null);
|
|
6955
|
+
pack.session.set("releaseId", previewContext?.releaseId ?? null);
|
|
6526
6956
|
pack.session.set("pageDraft", previewContext?.pageDraft ?? null);
|
|
6527
6957
|
pack.session.set(
|
|
6528
6958
|
"siteSettingsDraft",
|
|
@@ -6991,10 +7421,15 @@ export {
|
|
|
6991
7421
|
PackTestProvider,
|
|
6992
7422
|
PackTestRoute,
|
|
6993
7423
|
PackTestSession,
|
|
7424
|
+
buildIdentityBundle,
|
|
6994
7425
|
createPackClient,
|
|
7426
|
+
getConditionEvaluator,
|
|
6995
7427
|
handleRequest,
|
|
7428
|
+
isConditionRegistered,
|
|
6996
7429
|
action as previewModeAction,
|
|
6997
7430
|
loader as previewModeLoader,
|
|
7431
|
+
registerConditionEvaluator,
|
|
7432
|
+
resolveRulesTree,
|
|
6998
7433
|
useAbTest,
|
|
6999
7434
|
useAbTestHandle,
|
|
7000
7435
|
useAbTestId,
|