@runbandit/sdk 3.1.2 → 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/chunk-BG5L4PBC.js +319 -0
- package/dist/chunk-BG5L4PBC.js.map +1 -0
- package/dist/decision-cookie-Mb4G_lTe.d.ts +483 -0
- package/dist/index.d.ts +5 -469
- package/dist/index.js +24 -249
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +48 -0
- package/dist/server.js +195 -0
- package/dist/server.js.map +1 -0
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DECISIONS_COOKIE,
|
|
3
|
+
DEFAULT_SESSION_TTL_MS,
|
|
4
|
+
SESSION_CREATED_AT_KEY,
|
|
5
|
+
SESSION_ID_KEY,
|
|
6
|
+
SnapshotClient,
|
|
7
|
+
VISITOR_COOKIE,
|
|
8
|
+
VISITOR_ID_KEY,
|
|
9
|
+
defaultSessionStorage,
|
|
10
|
+
generateId,
|
|
11
|
+
getOrCreateSessionId,
|
|
12
|
+
getOrCreateVisitorId,
|
|
13
|
+
parseDecisions,
|
|
14
|
+
pickTreatment,
|
|
15
|
+
readCookie,
|
|
16
|
+
serializeDecisions
|
|
17
|
+
} from "./chunk-BG5L4PBC.js";
|
|
18
|
+
|
|
1
19
|
// ../shared/dist/types.js
|
|
2
20
|
var ExperimentStatus;
|
|
3
21
|
(function(ExperimentStatus2) {
|
|
@@ -898,54 +916,6 @@ function platformById(id) {
|
|
|
898
916
|
return SITE_PLATFORMS.find((platform) => platform.id === id);
|
|
899
917
|
}
|
|
900
918
|
|
|
901
|
-
// src/session.ts
|
|
902
|
-
var SESSION_ID_KEY = "bandits_session_id";
|
|
903
|
-
var SESSION_CREATED_AT_KEY = "bandits_session_created_at";
|
|
904
|
-
var VISITOR_ID_KEY = "bandits_visitor_id";
|
|
905
|
-
var DEFAULT_SESSION_TTL_MS = 864e5;
|
|
906
|
-
function generateId() {
|
|
907
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
908
|
-
return crypto.randomUUID();
|
|
909
|
-
}
|
|
910
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
911
|
-
const r = Math.random() * 16 | 0;
|
|
912
|
-
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
function getOrCreateSessionId(options = {}) {
|
|
916
|
-
const { storage, now = () => Date.now(), ttlMs = DEFAULT_SESSION_TTL_MS } = options;
|
|
917
|
-
if (!storage) return generateId();
|
|
918
|
-
try {
|
|
919
|
-
const stored = storage.getItem(SESSION_ID_KEY);
|
|
920
|
-
const storedAt = storage.getItem(SESSION_CREATED_AT_KEY);
|
|
921
|
-
if (stored && storedAt) {
|
|
922
|
-
const age = now() - parseInt(storedAt, 10);
|
|
923
|
-
if (age < ttlMs) return stored;
|
|
924
|
-
}
|
|
925
|
-
const id = generateId();
|
|
926
|
-
storage.setItem(SESSION_ID_KEY, id);
|
|
927
|
-
storage.setItem(SESSION_CREATED_AT_KEY, String(now()));
|
|
928
|
-
return id;
|
|
929
|
-
} catch {
|
|
930
|
-
return generateId();
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
function getOrCreateVisitorId(storage) {
|
|
934
|
-
if (!storage) return generateId();
|
|
935
|
-
try {
|
|
936
|
-
const stored = storage.getItem(VISITOR_ID_KEY);
|
|
937
|
-
if (stored) return stored;
|
|
938
|
-
const id = generateId();
|
|
939
|
-
storage.setItem(VISITOR_ID_KEY, id);
|
|
940
|
-
return id;
|
|
941
|
-
} catch {
|
|
942
|
-
return generateId();
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
function defaultSessionStorage() {
|
|
946
|
-
return typeof window !== "undefined" && window.localStorage ? window.localStorage : null;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
919
|
// src/client.ts
|
|
950
920
|
var BanditsRequestError = class extends Error {
|
|
951
921
|
status;
|
|
@@ -979,7 +949,7 @@ var BanditsClient = class {
|
|
|
979
949
|
...config
|
|
980
950
|
};
|
|
981
951
|
this.sessionId = this.getOrCreateSessionId();
|
|
982
|
-
this.visitorId = getOrCreateVisitorId(defaultSessionStorage());
|
|
952
|
+
this.visitorId = config.visitorId ?? getOrCreateVisitorId(defaultSessionStorage());
|
|
983
953
|
this.startBatchFlush();
|
|
984
954
|
}
|
|
985
955
|
async getAssignment(experimentId, userId = this.visitorId, context) {
|
|
@@ -1198,206 +1168,6 @@ var BanditsClient = class {
|
|
|
1198
1168
|
}
|
|
1199
1169
|
};
|
|
1200
1170
|
|
|
1201
|
-
// src/snapshot-cache.ts
|
|
1202
|
-
var STORAGE_KEY = "bandits_snapshot";
|
|
1203
|
-
var REPORT_QUEUE_KEY = "bandits_pending_reports";
|
|
1204
|
-
var DECISIONS_KEY = "bandits_client_assignments";
|
|
1205
|
-
function pickTreatment(treatments, draw) {
|
|
1206
|
-
if (treatments.length === 0) return null;
|
|
1207
|
-
let cumulative = 0;
|
|
1208
|
-
for (const treatment of treatments) {
|
|
1209
|
-
cumulative += treatment.pct;
|
|
1210
|
-
if (draw < cumulative) return treatment;
|
|
1211
|
-
}
|
|
1212
|
-
return treatments[treatments.length - 1];
|
|
1213
|
-
}
|
|
1214
|
-
var DEFAULT_ASSIGNMENT_TTL_SECONDS = 86400;
|
|
1215
|
-
function randomId() {
|
|
1216
|
-
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1217
|
-
let id = "";
|
|
1218
|
-
for (let i = 0; i < 24; i++) {
|
|
1219
|
-
id += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
1220
|
-
}
|
|
1221
|
-
return id;
|
|
1222
|
-
}
|
|
1223
|
-
var SnapshotClient = class {
|
|
1224
|
-
config;
|
|
1225
|
-
storage;
|
|
1226
|
-
now;
|
|
1227
|
-
fetchImpl;
|
|
1228
|
-
snapshot = null;
|
|
1229
|
-
queue = [];
|
|
1230
|
-
sessionId;
|
|
1231
|
-
inFlight = null;
|
|
1232
|
-
constructor(config) {
|
|
1233
|
-
this.config = config;
|
|
1234
|
-
this.now = config.now ?? (() => Date.now());
|
|
1235
|
-
this.fetchImpl = config.fetchImpl ?? ((...args) => fetch(...args));
|
|
1236
|
-
this.storage = config.storage ?? (typeof window !== "undefined" && window.localStorage ? window.localStorage : null);
|
|
1237
|
-
this.queue = this.readQueue();
|
|
1238
|
-
this.sessionId = getOrCreateSessionId({
|
|
1239
|
-
storage: this.storage ?? defaultSessionStorage(),
|
|
1240
|
-
now: this.now,
|
|
1241
|
-
ttlMs: config.sessionTtl
|
|
1242
|
-
});
|
|
1243
|
-
if (config.inlineSnapshot) {
|
|
1244
|
-
this.snapshot = config.inlineSnapshot;
|
|
1245
|
-
this.persistSnapshot(config.inlineSnapshot);
|
|
1246
|
-
} else {
|
|
1247
|
-
this.snapshot = this.readSnapshot();
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
getAssignmentSync(experimentId, userId) {
|
|
1251
|
-
const snapshot = this.freshSnapshot();
|
|
1252
|
-
if (!snapshot) return null;
|
|
1253
|
-
const experiment = snapshot.experiments[experimentId];
|
|
1254
|
-
if (!experiment || experiment.mode !== "snapshot") return null;
|
|
1255
|
-
const key = `${experimentId}:${userId}`;
|
|
1256
|
-
const decisions = this.readDecisions();
|
|
1257
|
-
const stored = decisions[key];
|
|
1258
|
-
if (stored && stored.expiresAt > this.now()) {
|
|
1259
|
-
const held = experiment.treatments.find((t) => t.id === stored.treatmentId);
|
|
1260
|
-
if (held) {
|
|
1261
|
-
return {
|
|
1262
|
-
clientAssignmentId: stored.clientAssignmentId,
|
|
1263
|
-
experimentId,
|
|
1264
|
-
treatmentId: held.id,
|
|
1265
|
-
config: held.config
|
|
1266
|
-
};
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
const treatment = pickTreatment(experiment.treatments, Math.random());
|
|
1270
|
-
if (!treatment) return null;
|
|
1271
|
-
const clientAssignmentId = randomId();
|
|
1272
|
-
const ttlSeconds = experiment.assignmentTtl ?? DEFAULT_ASSIGNMENT_TTL_SECONDS;
|
|
1273
|
-
decisions[key] = {
|
|
1274
|
-
clientAssignmentId,
|
|
1275
|
-
treatmentId: treatment.id,
|
|
1276
|
-
expiresAt: this.now() + ttlSeconds * 1e3
|
|
1277
|
-
};
|
|
1278
|
-
this.writeDecisions(decisions);
|
|
1279
|
-
this.queue.push({
|
|
1280
|
-
clientAssignmentId,
|
|
1281
|
-
experimentId,
|
|
1282
|
-
treatmentId: treatment.id,
|
|
1283
|
-
userId,
|
|
1284
|
-
sessionId: this.sessionId
|
|
1285
|
-
});
|
|
1286
|
-
this.persistQueue();
|
|
1287
|
-
return {
|
|
1288
|
-
clientAssignmentId,
|
|
1289
|
-
experimentId,
|
|
1290
|
-
treatmentId: treatment.id,
|
|
1291
|
-
config: treatment.config
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
async refresh() {
|
|
1295
|
-
const response = await this.fetchImpl(`${this.config.apiUrl}/snapshot`, {
|
|
1296
|
-
method: "GET",
|
|
1297
|
-
headers: { "x-api-key": this.config.publishableKey }
|
|
1298
|
-
});
|
|
1299
|
-
if (!response.ok) return;
|
|
1300
|
-
const snapshot = await response.json();
|
|
1301
|
-
this.snapshot = snapshot;
|
|
1302
|
-
this.persistSnapshot(snapshot);
|
|
1303
|
-
}
|
|
1304
|
-
async flush() {
|
|
1305
|
-
if (this.inFlight) return this.inFlight;
|
|
1306
|
-
this.inFlight = this.doFlush().finally(() => {
|
|
1307
|
-
this.inFlight = null;
|
|
1308
|
-
});
|
|
1309
|
-
return this.inFlight;
|
|
1310
|
-
}
|
|
1311
|
-
async doFlush() {
|
|
1312
|
-
if (this.queue.length === 0) return;
|
|
1313
|
-
const reports = this.queue.slice(0, 1e3);
|
|
1314
|
-
try {
|
|
1315
|
-
const response = await this.fetchImpl(`${this.config.apiUrl}/assignments/report`, {
|
|
1316
|
-
method: "POST",
|
|
1317
|
-
headers: {
|
|
1318
|
-
"content-type": "application/json",
|
|
1319
|
-
"x-api-key": this.config.publishableKey
|
|
1320
|
-
},
|
|
1321
|
-
body: JSON.stringify({ reports }),
|
|
1322
|
-
keepalive: true
|
|
1323
|
-
});
|
|
1324
|
-
if (!response.ok) return;
|
|
1325
|
-
} catch {
|
|
1326
|
-
return;
|
|
1327
|
-
}
|
|
1328
|
-
this.queue = this.queue.slice(reports.length);
|
|
1329
|
-
this.persistQueue();
|
|
1330
|
-
}
|
|
1331
|
-
getSessionId() {
|
|
1332
|
-
return this.sessionId;
|
|
1333
|
-
}
|
|
1334
|
-
pendingReportCount() {
|
|
1335
|
-
return this.queue.length;
|
|
1336
|
-
}
|
|
1337
|
-
freshSnapshot() {
|
|
1338
|
-
if (!this.snapshot) return null;
|
|
1339
|
-
if (this.snapshot.ttl === 0) return null;
|
|
1340
|
-
const ageMs = this.now() - this.snapshot.generatedAt;
|
|
1341
|
-
if (ageMs > this.snapshot.ttl * 1e3) return null;
|
|
1342
|
-
return this.snapshot;
|
|
1343
|
-
}
|
|
1344
|
-
readSnapshot() {
|
|
1345
|
-
if (!this.storage) return null;
|
|
1346
|
-
try {
|
|
1347
|
-
const raw = this.storage.getItem(STORAGE_KEY);
|
|
1348
|
-
return raw ? JSON.parse(raw) : null;
|
|
1349
|
-
} catch {
|
|
1350
|
-
return null;
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
persistSnapshot(snapshot) {
|
|
1354
|
-
if (!this.storage) return;
|
|
1355
|
-
try {
|
|
1356
|
-
this.storage.setItem(STORAGE_KEY, JSON.stringify(snapshot));
|
|
1357
|
-
} catch {
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
readQueue() {
|
|
1361
|
-
if (!this.storage) return [];
|
|
1362
|
-
try {
|
|
1363
|
-
const raw = this.storage.getItem(REPORT_QUEUE_KEY);
|
|
1364
|
-
return raw ? JSON.parse(raw) : [];
|
|
1365
|
-
} catch {
|
|
1366
|
-
return [];
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
readDecisions() {
|
|
1370
|
-
if (!this.storage) return {};
|
|
1371
|
-
try {
|
|
1372
|
-
const raw = this.storage.getItem(DECISIONS_KEY);
|
|
1373
|
-
if (!raw) return {};
|
|
1374
|
-
const parsed = JSON.parse(raw);
|
|
1375
|
-
const now = this.now();
|
|
1376
|
-
const live = {};
|
|
1377
|
-
for (const [k, d] of Object.entries(parsed)) {
|
|
1378
|
-
if (d && d.expiresAt > now) live[k] = d;
|
|
1379
|
-
}
|
|
1380
|
-
return live;
|
|
1381
|
-
} catch {
|
|
1382
|
-
return {};
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
writeDecisions(decisions) {
|
|
1386
|
-
if (!this.storage) return;
|
|
1387
|
-
try {
|
|
1388
|
-
this.storage.setItem(DECISIONS_KEY, JSON.stringify(decisions));
|
|
1389
|
-
} catch {
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
persistQueue() {
|
|
1393
|
-
if (!this.storage) return;
|
|
1394
|
-
try {
|
|
1395
|
-
this.storage.setItem(REPORT_QUEUE_KEY, JSON.stringify(this.queue));
|
|
1396
|
-
} catch {
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
};
|
|
1400
|
-
|
|
1401
1171
|
// src/email-links.ts
|
|
1402
1172
|
var UNTRACKABLE_SCHEME = /^(mailto:|tel:|sms:|javascript:|data:|#)/i;
|
|
1403
1173
|
var NOTRACK_ATTRIBUTE = "data-bandit-notrack";
|
|
@@ -1596,6 +1366,7 @@ export {
|
|
|
1596
1366
|
BanditsRequestError,
|
|
1597
1367
|
CONTENT_TYPE_BY_KIND,
|
|
1598
1368
|
ContentType,
|
|
1369
|
+
DECISIONS_COOKIE,
|
|
1599
1370
|
DEFAULT_HALF_LIFE_MS,
|
|
1600
1371
|
DEFAULT_SESSION_TTL_MS,
|
|
1601
1372
|
DEFAULT_WINDOW_MS,
|
|
@@ -1626,6 +1397,7 @@ export {
|
|
|
1626
1397
|
STOREFRONT_KINDS,
|
|
1627
1398
|
SUGGESTED_SITE_EXPERIMENTS,
|
|
1628
1399
|
SnapshotClient,
|
|
1400
|
+
VISITOR_COOKIE,
|
|
1629
1401
|
VISITOR_ID_KEY,
|
|
1630
1402
|
appendEmailToken,
|
|
1631
1403
|
buildSubdomainUrl,
|
|
@@ -1658,6 +1430,7 @@ export {
|
|
|
1658
1430
|
normalizeSiteTarget,
|
|
1659
1431
|
normalizeTreatmentWeights,
|
|
1660
1432
|
parseAlternative,
|
|
1433
|
+
parseDecisions,
|
|
1661
1434
|
parsePanelMessage,
|
|
1662
1435
|
parsePickerMessage,
|
|
1663
1436
|
parseSiteContent,
|
|
@@ -1667,6 +1440,7 @@ export {
|
|
|
1667
1440
|
pickStep,
|
|
1668
1441
|
pickTreatment,
|
|
1669
1442
|
platformById,
|
|
1443
|
+
readCookie,
|
|
1670
1444
|
renderEmail,
|
|
1671
1445
|
renderEmailDocument,
|
|
1672
1446
|
renderFromPlan,
|
|
@@ -1675,6 +1449,7 @@ export {
|
|
|
1675
1449
|
rewriteEmailLinks,
|
|
1676
1450
|
sampleCombination,
|
|
1677
1451
|
sanitizeMetadata,
|
|
1452
|
+
serializeDecisions,
|
|
1678
1453
|
serializeTreatmentConfig,
|
|
1679
1454
|
shouldRefreshPlan,
|
|
1680
1455
|
targetMatchesPage,
|