@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.d.ts +84 -1
- package/dist/index.js +710 -263
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,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(
|
|
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]
|
|
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 =
|
|
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
|
-
|
|
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,
|