@shipeasy/sdk 6.3.1 → 7.0.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/README.md +1 -4
- package/dist/client/index.d.mts +128 -22
- package/dist/client/index.d.ts +128 -22
- package/dist/client/index.js +238 -133
- package/dist/client/index.mjs +238 -133
- package/dist/openfeature-server/index.d.mts +117 -8
- package/dist/openfeature-server/index.d.ts +117 -8
- package/dist/openfeature-server/index.js +65 -33
- package/dist/openfeature-server/index.mjs +65 -33
- package/dist/openfeature-web/index.d.mts +89 -27
- package/dist/openfeature-web/index.d.ts +89 -27
- package/dist/server/index.d.mts +145 -21
- package/dist/server/index.d.ts +145 -21
- package/dist/server/index.js +305 -158
- package/dist/server/index.mjs +305 -158
- package/docs/skill/SKILL.md +14 -10
- package/package.json +3 -1
package/dist/client/index.js
CHANGED
|
@@ -110,53 +110,6 @@ function send(url) {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
// src/logger.ts
|
|
114
|
-
var LOG_LEVELS = ["silent", "error", "warn", "info", "debug"];
|
|
115
|
-
var RANK = {
|
|
116
|
-
silent: 0,
|
|
117
|
-
error: 1,
|
|
118
|
-
warn: 2,
|
|
119
|
-
info: 3,
|
|
120
|
-
debug: 4
|
|
121
|
-
};
|
|
122
|
-
var currentLevel = "warn";
|
|
123
|
-
function setLogLevel(level) {
|
|
124
|
-
if (typeof level === "string" && Object.prototype.hasOwnProperty.call(RANK, level)) {
|
|
125
|
-
currentLevel = level;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function write(method, args) {
|
|
129
|
-
try {
|
|
130
|
-
const c = globalThis.console;
|
|
131
|
-
if (!c) return;
|
|
132
|
-
const fn = c[method] ?? c.log;
|
|
133
|
-
if (typeof fn === "function") fn.call(c, ...args);
|
|
134
|
-
} catch {
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
var logger = {
|
|
138
|
-
error(...args) {
|
|
139
|
-
if (RANK[currentLevel] >= RANK.error) write("error", args);
|
|
140
|
-
},
|
|
141
|
-
warn(...args) {
|
|
142
|
-
if (RANK[currentLevel] >= RANK.warn) write("warn", args);
|
|
143
|
-
},
|
|
144
|
-
info(...args) {
|
|
145
|
-
if (RANK[currentLevel] >= RANK.info) write("info", args);
|
|
146
|
-
},
|
|
147
|
-
debug(...args) {
|
|
148
|
-
if (RANK[currentLevel] >= RANK.debug) write("debug", args);
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
function safeRun(label, fallback, fn) {
|
|
152
|
-
try {
|
|
153
|
-
return fn();
|
|
154
|
-
} catch (err) {
|
|
155
|
-
logger.error(`[shipeasy] ${label} failed \u2014 returning safe default:`, String(err));
|
|
156
|
-
return fallback;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
113
|
// src/see/core.ts
|
|
161
114
|
var SEE_MAX_MESSAGE = 500;
|
|
162
115
|
var SEE_MAX_STACK = 8e3;
|
|
@@ -274,7 +227,7 @@ function captureCallsiteStack() {
|
|
|
274
227
|
const kept = lines.slice(1).filter((l) => !/@shipeasy[\\/]sdk|see[\\/]core|captureCallsiteStack|\bsee\b\s*\(/.test(l));
|
|
275
228
|
return kept.length ? kept.join("\n") : void 0;
|
|
276
229
|
}
|
|
277
|
-
function buildSeeEvent(problem, consequence, extras,
|
|
230
|
+
function buildSeeEvent(problem, consequence, extras, ctx2, kindOverride, correlationId) {
|
|
278
231
|
let errorType;
|
|
279
232
|
let message;
|
|
280
233
|
let stack;
|
|
@@ -302,8 +255,8 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlat
|
|
|
302
255
|
message: truncate(message, SEE_MAX_MESSAGE),
|
|
303
256
|
subject: consequence.subject,
|
|
304
257
|
outcome: consequence.outcome,
|
|
305
|
-
side:
|
|
306
|
-
sdk_version:
|
|
258
|
+
side: ctx2.side,
|
|
259
|
+
sdk_version: ctx2.sdkVersion,
|
|
307
260
|
ts: Date.now()
|
|
308
261
|
};
|
|
309
262
|
if (stack) ev.stack = truncate(stack, SEE_MAX_STACK);
|
|
@@ -312,10 +265,10 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlat
|
|
|
312
265
|
if (causedBy) ev.caused_by = causedBy;
|
|
313
266
|
const cleanExtras = sanitizeExtras(extras);
|
|
314
267
|
if (cleanExtras) ev.extras = cleanExtras;
|
|
315
|
-
if (
|
|
316
|
-
if (
|
|
317
|
-
if (
|
|
318
|
-
if (
|
|
268
|
+
if (ctx2.url) ev.url = truncate(ctx2.url, SEE_MAX_SUBJECT);
|
|
269
|
+
if (ctx2.userId) ev.user_id = ctx2.userId;
|
|
270
|
+
if (ctx2.anonId) ev.anonymous_id = ctx2.anonId;
|
|
271
|
+
if (ctx2.env) ev.env = ctx2.env;
|
|
319
272
|
markReported(problem, ev);
|
|
320
273
|
return ev;
|
|
321
274
|
}
|
|
@@ -408,6 +361,98 @@ var SeeLimiter = class {
|
|
|
408
361
|
}
|
|
409
362
|
};
|
|
410
363
|
|
|
364
|
+
// src/internal-report.ts
|
|
365
|
+
var INGEST_URL = "https://api.shipeasy.ai/collect";
|
|
366
|
+
var PLACEHOLDER_KEY = "sdk_client_REPLACE_WITH_SHIPEASY_INTERNAL_ERROR_KEY";
|
|
367
|
+
var INGEST_KEY = "sdk_client_00bd4608a03e4084922978f9522614d5";
|
|
368
|
+
function keyConfigured() {
|
|
369
|
+
return !!INGEST_KEY && INGEST_KEY !== PLACEHOLDER_KEY;
|
|
370
|
+
}
|
|
371
|
+
var OUTCOME = "returned a safe default";
|
|
372
|
+
var SDK_ID = "ts";
|
|
373
|
+
var ctx = null;
|
|
374
|
+
var limiter = new SeeLimiter();
|
|
375
|
+
function setInternalReportContext(c) {
|
|
376
|
+
ctx = { side: c.side, sdkVersion: c.sdkVersion, enabled: c.enabled !== false };
|
|
377
|
+
}
|
|
378
|
+
function reportInternalError(label, err) {
|
|
379
|
+
try {
|
|
380
|
+
if (!ctx || !ctx.enabled || !keyConfigured()) return;
|
|
381
|
+
const ev = buildSeeEvent(
|
|
382
|
+
err,
|
|
383
|
+
causesThe(label).to(OUTCOME),
|
|
384
|
+
{ sdk: SDK_ID },
|
|
385
|
+
{ side: ctx.side, sdkVersion: ctx.sdkVersion },
|
|
386
|
+
"caught"
|
|
387
|
+
);
|
|
388
|
+
if (!limiter.shouldSend(ev)) return;
|
|
389
|
+
send2(INGEST_URL, INGEST_KEY, JSON.stringify({ events: [ev] }));
|
|
390
|
+
} catch {
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function send2(url, key, body) {
|
|
394
|
+
try {
|
|
395
|
+
const f = globalThis.fetch;
|
|
396
|
+
if (typeof f !== "function") return;
|
|
397
|
+
void f(url, {
|
|
398
|
+
method: "POST",
|
|
399
|
+
headers: { "X-SDK-Key": key, "Content-Type": "text/plain" },
|
|
400
|
+
body,
|
|
401
|
+
keepalive: true
|
|
402
|
+
}).catch(() => {
|
|
403
|
+
});
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/logger.ts
|
|
409
|
+
var LOG_LEVELS = ["silent", "error", "warn", "info", "debug"];
|
|
410
|
+
var RANK = {
|
|
411
|
+
silent: 0,
|
|
412
|
+
error: 1,
|
|
413
|
+
warn: 2,
|
|
414
|
+
info: 3,
|
|
415
|
+
debug: 4
|
|
416
|
+
};
|
|
417
|
+
var currentLevel = "warn";
|
|
418
|
+
function setLogLevel(level) {
|
|
419
|
+
if (typeof level === "string" && Object.prototype.hasOwnProperty.call(RANK, level)) {
|
|
420
|
+
currentLevel = level;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function write(method, args) {
|
|
424
|
+
try {
|
|
425
|
+
const c = globalThis.console;
|
|
426
|
+
if (!c) return;
|
|
427
|
+
const fn = c[method] ?? c.log;
|
|
428
|
+
if (typeof fn === "function") fn.call(c, ...args);
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
var logger = {
|
|
433
|
+
error(...args) {
|
|
434
|
+
if (RANK[currentLevel] >= RANK.error) write("error", args);
|
|
435
|
+
},
|
|
436
|
+
warn(...args) {
|
|
437
|
+
if (RANK[currentLevel] >= RANK.warn) write("warn", args);
|
|
438
|
+
},
|
|
439
|
+
info(...args) {
|
|
440
|
+
if (RANK[currentLevel] >= RANK.info) write("info", args);
|
|
441
|
+
},
|
|
442
|
+
debug(...args) {
|
|
443
|
+
if (RANK[currentLevel] >= RANK.debug) write("debug", args);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
function safeRun(label, fallback, fn) {
|
|
447
|
+
try {
|
|
448
|
+
return fn();
|
|
449
|
+
} catch (err) {
|
|
450
|
+
logger.error(`[shipeasy] ${label} failed \u2014 returning safe default:`, String(err));
|
|
451
|
+
reportInternalError(label, err);
|
|
452
|
+
return fallback;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
411
456
|
// src/client/index.ts
|
|
412
457
|
var version = "4.0.0";
|
|
413
458
|
var FLAG_REASONS = [
|
|
@@ -418,6 +463,40 @@ var FLAG_REASONS = [
|
|
|
418
463
|
"RULE_MATCH",
|
|
419
464
|
"DEFAULT"
|
|
420
465
|
];
|
|
466
|
+
var AssignmentImpl = class {
|
|
467
|
+
constructor(name, group, params) {
|
|
468
|
+
this.name = name;
|
|
469
|
+
this.group = group;
|
|
470
|
+
this.params = params;
|
|
471
|
+
}
|
|
472
|
+
name;
|
|
473
|
+
group;
|
|
474
|
+
params;
|
|
475
|
+
get enrolled() {
|
|
476
|
+
return this.group !== null;
|
|
477
|
+
}
|
|
478
|
+
get(field, fallback) {
|
|
479
|
+
const v = this.params[field];
|
|
480
|
+
return v === void 0 ? fallback : v;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
function hasDomEvents() {
|
|
484
|
+
return typeof window !== "undefined" && typeof window.addEventListener === "function";
|
|
485
|
+
}
|
|
486
|
+
function onWindow(type, handler, opts) {
|
|
487
|
+
try {
|
|
488
|
+
if (hasDomEvents()) window.addEventListener(type, handler, opts);
|
|
489
|
+
} catch {
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function onDocument(type, handler, opts) {
|
|
493
|
+
try {
|
|
494
|
+
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
|
495
|
+
document.addEventListener(type, handler, opts);
|
|
496
|
+
}
|
|
497
|
+
} catch {
|
|
498
|
+
}
|
|
499
|
+
}
|
|
421
500
|
var FLUSH_INTERVAL_MS = 5e3;
|
|
422
501
|
var MAX_BUFFER = 100;
|
|
423
502
|
var ANON_ID_KEY = "__se_anon_id";
|
|
@@ -429,8 +508,8 @@ var EventBuffer = class {
|
|
|
429
508
|
this.sdkKey = sdkKey;
|
|
430
509
|
if (typeof window !== "undefined") {
|
|
431
510
|
this.timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
432
|
-
|
|
433
|
-
|
|
511
|
+
onWindow("beforeunload", () => this.flush());
|
|
512
|
+
onDocument("visibilitychange", () => {
|
|
434
513
|
if (document.visibilityState === "hidden") this.flush(true);
|
|
435
514
|
});
|
|
436
515
|
try {
|
|
@@ -975,6 +1054,10 @@ var Engine = class _Engine {
|
|
|
975
1054
|
env;
|
|
976
1055
|
evalResult = null;
|
|
977
1056
|
anonId;
|
|
1057
|
+
// Resolves once a caller-supplied anonymousStore has been consulted (persisted
|
|
1058
|
+
// id adopted, or the minted id written back). identify() awaits it so the
|
|
1059
|
+
// stable id is settled before the first /sdk/evaluate. null when no store.
|
|
1060
|
+
anonReady = null;
|
|
978
1061
|
userId = "";
|
|
979
1062
|
buffer;
|
|
980
1063
|
telemetry;
|
|
@@ -1002,9 +1085,14 @@ var Engine = class _Engine {
|
|
|
1002
1085
|
constructor(opts) {
|
|
1003
1086
|
setLogLevel(opts.logLevel);
|
|
1004
1087
|
this.sdkKey = opts.sdkKey;
|
|
1005
|
-
this.baseUrl = (opts.baseUrl ?? "https://
|
|
1088
|
+
this.baseUrl = (opts.baseUrl ?? "https://api.shipeasy.ai").replace(/\/$/, "");
|
|
1006
1089
|
this.env = opts.env ?? "prod";
|
|
1007
1090
|
this.testMode = opts.testMode === true;
|
|
1091
|
+
setInternalReportContext({
|
|
1092
|
+
side: "client",
|
|
1093
|
+
sdkVersion: version,
|
|
1094
|
+
enabled: !this.testMode && opts.disableInternalErrorReporting !== true
|
|
1095
|
+
});
|
|
1008
1096
|
this.autoGuardrails = opts.autoGuardrails !== false;
|
|
1009
1097
|
this.autoCollectAlways = opts.autoCollectAlways === true;
|
|
1010
1098
|
this.disableAutoExposure = opts.disableAutoExposure === true;
|
|
@@ -1017,6 +1105,9 @@ var Engine = class _Engine {
|
|
|
1017
1105
|
engagement: g.engagement ?? this.autoGuardrails
|
|
1018
1106
|
};
|
|
1019
1107
|
this.anonId = getOrCreateAnonId();
|
|
1108
|
+
if (opts.anonymousStore && !this.testMode) {
|
|
1109
|
+
this.anonReady = this.hydrateAnonFromStore(opts.anonymousStore);
|
|
1110
|
+
}
|
|
1020
1111
|
this.buffer = new EventBuffer(`${this.baseUrl}/collect`, this.sdkKey);
|
|
1021
1112
|
this.telemetry = new Telemetry({
|
|
1022
1113
|
endpoint: opts.telemetryUrl ?? DEFAULT_TELEMETRY_URL,
|
|
@@ -1032,6 +1123,23 @@ var Engine = class _Engine {
|
|
|
1032
1123
|
void this.buffer.flushPendingAlias();
|
|
1033
1124
|
}
|
|
1034
1125
|
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Consult the caller-supplied {@link AnonymousStore}: adopt a persisted id so
|
|
1128
|
+
* the visitor buckets identically across app launches, or persist the id just
|
|
1129
|
+
* minted by getOrCreateAnonId() on a fresh device. Best-effort — any store
|
|
1130
|
+
* failure leaves the in-memory id in place.
|
|
1131
|
+
*/
|
|
1132
|
+
async hydrateAnonFromStore(store) {
|
|
1133
|
+
try {
|
|
1134
|
+
const stored = await store.get(ANON_ID_KEY);
|
|
1135
|
+
if (typeof stored === "string" && stored) {
|
|
1136
|
+
this.anonId = stored;
|
|
1137
|
+
} else {
|
|
1138
|
+
await store.set(ANON_ID_KEY, this.anonId);
|
|
1139
|
+
}
|
|
1140
|
+
} catch {
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1035
1143
|
/**
|
|
1036
1144
|
* Build a no-network, immediately-usable browser client for tests
|
|
1037
1145
|
* (Statsig-style). identify() is a no-op (never calls /sdk/evaluate), track()
|
|
@@ -1059,6 +1167,10 @@ var Engine = class _Engine {
|
|
|
1059
1167
|
this.notify();
|
|
1060
1168
|
return;
|
|
1061
1169
|
}
|
|
1170
|
+
if (this.anonReady) {
|
|
1171
|
+
await this.anonReady;
|
|
1172
|
+
this.anonReady = null;
|
|
1173
|
+
}
|
|
1062
1174
|
const seq = ++this.identifySeq;
|
|
1063
1175
|
const prevUserId = this.userId;
|
|
1064
1176
|
if (user.user_id !== void 0) this.userId = user.user_id;
|
|
@@ -1216,50 +1328,37 @@ var Engine = class _Engine {
|
|
|
1216
1328
|
return void 0;
|
|
1217
1329
|
}
|
|
1218
1330
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1331
|
+
/**
|
|
1332
|
+
* Assign the visitor within a universe: `universe("checkout").assign()`. A
|
|
1333
|
+
* universe is a mutual-exclusion pool, so the visitor lands in ≤1 experiment;
|
|
1334
|
+
* the returned {@link Assignment} exposes `.group` / `.get(field, fallback)`
|
|
1335
|
+
* and auto-logs one exposure when enrolled (unless `disableAutoExposure` or
|
|
1336
|
+
* `assign({ logExposure: false })`). An un-enrolled visitor still resolves
|
|
1337
|
+
* `get()` to the universe defaults. This is the sole experiment read path —
|
|
1338
|
+
* there is no `getExperiment` (ask a universe, not an experiment).
|
|
1339
|
+
*/
|
|
1340
|
+
universe(name) {
|
|
1341
|
+
return { assign: (opts) => this.assignUniverse(name, opts) };
|
|
1342
|
+
}
|
|
1343
|
+
assignUniverse(name, opts) {
|
|
1222
1344
|
this.telemetry.emit("experiment", name);
|
|
1223
|
-
const
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
params
|
|
1227
|
-
};
|
|
1228
|
-
const pov = this.experimentOverrides.get(name);
|
|
1229
|
-
if (pov) {
|
|
1230
|
-
const variantParams = variants?.[pov.group];
|
|
1231
|
-
const params = { ...defaultParams, ...pov.params, ...variantParams ?? {} };
|
|
1232
|
-
return { inExperiment: true, group: pov.group, params };
|
|
1345
|
+
const defaults = this.evalResult?.universes?.[name]?.defaults ?? {};
|
|
1346
|
+
for (const [expName, pov] of this.experimentOverrides) {
|
|
1347
|
+
if (this.evalResult?.experiments[expName]?.universe !== name) continue;
|
|
1348
|
+
return new AssignmentImpl(expName, pov.group, { ...defaults, ...pov.params });
|
|
1233
1349
|
}
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
return { inExperiment: true, group: ov, params };
|
|
1350
|
+
for (const [expName, entry] of Object.entries(this.evalResult?.experiments ?? {})) {
|
|
1351
|
+
if (entry.universe !== name) continue;
|
|
1352
|
+
const ov = readExpOverride(expName);
|
|
1353
|
+
if (ov !== null) return new AssignmentImpl(expName, ov, { ...defaults, ...entry.params });
|
|
1239
1354
|
}
|
|
1240
|
-
const entry
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
try {
|
|
1246
|
-
return { inExperiment: true, group: entry.group, params: decode(entry.params) };
|
|
1247
|
-
} catch (err) {
|
|
1248
|
-
logger.warn(`[shipeasy] getExperiment('${name}') decode failed:`, String(err));
|
|
1249
|
-
return notIn;
|
|
1355
|
+
for (const [expName, entry] of Object.entries(this.evalResult?.experiments ?? {})) {
|
|
1356
|
+
if (entry.universe !== name || !entry.inExperiment) continue;
|
|
1357
|
+
const shouldLog = opts?.logExposure ?? !this.disableAutoExposure;
|
|
1358
|
+
if (shouldLog) this.buffer.pushExposure(expName, entry.group, this.userId, this.anonId);
|
|
1359
|
+
return new AssignmentImpl(expName, entry.group, entry.params);
|
|
1250
1360
|
}
|
|
1251
|
-
|
|
1252
|
-
/**
|
|
1253
|
-
* Manually log an exposure for an enrolled experiment (Statsig's
|
|
1254
|
-
* `manuallyLogExposure`). Reads the cached eval result; if the visitor is in
|
|
1255
|
-
* the experiment, pushes the session-deduped exposure. Pair this with the
|
|
1256
|
-
* render of the treatment when reading with `{ logExposure: false }` (or
|
|
1257
|
-
* `disableAutoExposure: true`). No-op if the visitor isn't enrolled.
|
|
1258
|
-
*/
|
|
1259
|
-
logExposure(name) {
|
|
1260
|
-
const entry = this.evalResult?.experiments[name];
|
|
1261
|
-
if (!entry || !entry.inExperiment) return;
|
|
1262
|
-
this.buffer.pushExposure(name, entry.group, this.userId, this.anonId);
|
|
1361
|
+
return new AssignmentImpl(null, null, defaults);
|
|
1263
1362
|
}
|
|
1264
1363
|
/**
|
|
1265
1364
|
* Subscribe to state changes — fires after identify() completes and on
|
|
@@ -1268,7 +1367,7 @@ var Engine = class _Engine {
|
|
|
1268
1367
|
*/
|
|
1269
1368
|
subscribe(listener) {
|
|
1270
1369
|
this.listeners.add(listener);
|
|
1271
|
-
if (!this.overrideListenerInstalled &&
|
|
1370
|
+
if (!this.overrideListenerInstalled && hasDomEvents()) {
|
|
1272
1371
|
this.overrideListenerInstalled = true;
|
|
1273
1372
|
window.addEventListener("se:override:change", this.onOverrideChange);
|
|
1274
1373
|
}
|
|
@@ -1284,9 +1383,11 @@ var Engine = class _Engine {
|
|
|
1284
1383
|
if (typeof window === "undefined") return null;
|
|
1285
1384
|
const bridge = {
|
|
1286
1385
|
getFlag: (n) => this.getFlag(n),
|
|
1386
|
+
// Devtools reads assignments by experiment name straight off the eval
|
|
1387
|
+
// result (the public read path is universe-first; this is display-only).
|
|
1287
1388
|
getExperiment: (n) => {
|
|
1288
|
-
const
|
|
1289
|
-
return { inExperiment:
|
|
1389
|
+
const entry = this.evalResult?.experiments[n];
|
|
1390
|
+
return { inExperiment: entry?.inExperiment ?? false, group: entry?.group ?? "control" };
|
|
1290
1391
|
},
|
|
1291
1392
|
getConfig: (n) => this.getConfig(n)
|
|
1292
1393
|
};
|
|
@@ -1409,7 +1510,7 @@ function loadDevtools(opts = {}) {
|
|
|
1409
1510
|
}
|
|
1410
1511
|
}
|
|
1411
1512
|
function attachDevtools(client, opts = {}) {
|
|
1412
|
-
if (
|
|
1513
|
+
if (!hasDomEvents()) return () => {
|
|
1413
1514
|
};
|
|
1414
1515
|
const hotkey = opts.hotkey ?? "Shift+Alt+S";
|
|
1415
1516
|
const parts = hotkey.split("+");
|
|
@@ -1453,9 +1554,11 @@ function shipeasy(opts) {
|
|
|
1453
1554
|
autoGuardrailGroups: groups,
|
|
1454
1555
|
autoCollectAlways: acObj?.always === true,
|
|
1455
1556
|
disableTelemetry: opts.disableTelemetry,
|
|
1557
|
+
disableInternalErrorReporting: opts.disableInternalErrorReporting,
|
|
1456
1558
|
disableAutoExposure: opts.disableAutoExposure,
|
|
1457
1559
|
privateAttributes: opts.privateAttributes,
|
|
1458
|
-
stickyBucketing: opts.stickyBucketing
|
|
1560
|
+
stickyBucketing: opts.stickyBucketing,
|
|
1561
|
+
anonymousStore: opts.anonymousStore
|
|
1459
1562
|
});
|
|
1460
1563
|
injectI18nLoader(opts.clientKey, baseUrl, opts.i18nProfile);
|
|
1461
1564
|
flags.notifyMounted();
|
|
@@ -1537,7 +1640,7 @@ var _mountedAndReady = false;
|
|
|
1537
1640
|
var _standaloneListeners = /* @__PURE__ */ new Set();
|
|
1538
1641
|
var _standaloneOverrideWired = false;
|
|
1539
1642
|
function wireStandaloneOverride() {
|
|
1540
|
-
if (_standaloneOverrideWired ||
|
|
1643
|
+
if (_standaloneOverrideWired || !hasDomEvents()) return;
|
|
1541
1644
|
_standaloneOverrideWired = true;
|
|
1542
1645
|
window.addEventListener("se:override:change", () => {
|
|
1543
1646
|
for (const cb of _standaloneListeners) cb();
|
|
@@ -1603,21 +1706,22 @@ var flags = {
|
|
|
1603
1706
|
}
|
|
1604
1707
|
});
|
|
1605
1708
|
},
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1709
|
+
/**
|
|
1710
|
+
* Assign the visitor within a universe: `flags.universe("checkout").assign()`.
|
|
1711
|
+
* A universe is a mutual-exclusion pool, so the visitor lands in ≤1 experiment;
|
|
1712
|
+
* the returned {@link Assignment} exposes `.group` / `.get(field, fallback)`
|
|
1713
|
+
* and auto-logs one exposure when enrolled. Before configure() (or on error)
|
|
1714
|
+
* returns a safe not-enrolled handle. Replaces the removed `getExperiment` —
|
|
1715
|
+
* read experiments by universe, never by name.
|
|
1716
|
+
*/
|
|
1717
|
+
universe(name) {
|
|
1718
|
+
return {
|
|
1719
|
+
assign: (opts) => safeRun(
|
|
1720
|
+
"flags.universe.assign",
|
|
1721
|
+
new AssignmentImpl(null, null, {}),
|
|
1722
|
+
() => _client ? _client.universe(name).assign(opts) : new AssignmentImpl(null, null, {})
|
|
1723
|
+
)
|
|
1611
1724
|
};
|
|
1612
|
-
return safeRun("flags.getExperiment", fallback, () => {
|
|
1613
|
-
if (!_client) return fallback;
|
|
1614
|
-
return typeof decodeOrOpts === "function" ? _client.getExperiment(name, defaultParams, decodeOrOpts, variants) : _client.getExperiment(name, defaultParams, decodeOrOpts ?? {});
|
|
1615
|
-
});
|
|
1616
|
-
},
|
|
1617
|
-
/** Manually log an exposure for an enrolled experiment. See
|
|
1618
|
-
* {@link Engine.logExposure}. No-op before configure(). */
|
|
1619
|
-
logExposure(name) {
|
|
1620
|
-
safeRun("flags.logExposure", void 0, () => _client?.logExposure(name));
|
|
1621
1725
|
},
|
|
1622
1726
|
track(eventName, props) {
|
|
1623
1727
|
safeRun("flags.track", void 0, () => _client?.track(eventName, props));
|
|
@@ -1723,13 +1827,20 @@ var Client = class {
|
|
|
1723
1827
|
() => this.engine.getConfig(name, decodeOrOpts)
|
|
1724
1828
|
);
|
|
1725
1829
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1830
|
+
/**
|
|
1831
|
+
* Assign the visitor within a universe: `client.universe("checkout").assign()`.
|
|
1832
|
+
* A universe is a mutual-exclusion pool, so the visitor lands in ≤1 experiment;
|
|
1833
|
+
* returns an {@link Assignment} (`.group` / `.get(field, fallback)`) and
|
|
1834
|
+
* auto-logs one exposure when enrolled. Replaces the removed `getExperiment`.
|
|
1835
|
+
*/
|
|
1836
|
+
universe(name) {
|
|
1837
|
+
return {
|
|
1838
|
+
assign: (opts) => safeRun(
|
|
1839
|
+
"Client.universe.assign",
|
|
1840
|
+
new AssignmentImpl(null, null, {}),
|
|
1841
|
+
() => this.engine.universe(name).assign(opts)
|
|
1842
|
+
)
|
|
1843
|
+
};
|
|
1733
1844
|
}
|
|
1734
1845
|
/** Read a killswitch (not user-bound; mirrors {@link Engine.getKillswitch}). */
|
|
1735
1846
|
getKillswitch(name, switchKey) {
|
|
@@ -1744,15 +1855,6 @@ var Client = class {
|
|
|
1744
1855
|
track(eventName, props) {
|
|
1745
1856
|
safeRun("Client.track", void 0, () => this.engine.track(eventName, props));
|
|
1746
1857
|
}
|
|
1747
|
-
/**
|
|
1748
|
-
* Log an exposure for `name` at the treatment's render for the bound user.
|
|
1749
|
-
* Delegates to {@link Engine.logExposure} (no-op when the visitor isn't
|
|
1750
|
-
* enrolled). Pair with `getExperiment(name, …, { logExposure: false })` /
|
|
1751
|
-
* `disableAutoExposure` to log exposure exactly when you render.
|
|
1752
|
-
*/
|
|
1753
|
-
logExposure(name) {
|
|
1754
|
-
safeRun("Client.logExposure", void 0, () => this.engine.logExposure(name));
|
|
1755
|
-
}
|
|
1756
1858
|
};
|
|
1757
1859
|
function dispatchSee(problem, consequence, extras, kind) {
|
|
1758
1860
|
if (!_client) {
|
|
@@ -1978,6 +2080,7 @@ var i18n = {
|
|
|
1978
2080
|
whenReady() {
|
|
1979
2081
|
if (typeof window === "undefined") return Promise.resolve();
|
|
1980
2082
|
if (window.i18n?.locale) return Promise.resolve();
|
|
2083
|
+
if (!hasDomEvents()) return Promise.resolve();
|
|
1981
2084
|
return new Promise((resolve) => {
|
|
1982
2085
|
const handler = () => resolve();
|
|
1983
2086
|
window.addEventListener("se:i18n:ready", handler, { once: true });
|
|
@@ -1988,6 +2091,8 @@ var i18n = {
|
|
|
1988
2091
|
if (typeof window === "undefined") return () => {
|
|
1989
2092
|
};
|
|
1990
2093
|
if (window.i18n) return window.i18n.on("update", cb);
|
|
2094
|
+
if (!hasDomEvents()) return () => {
|
|
2095
|
+
};
|
|
1991
2096
|
let unsub = () => {
|
|
1992
2097
|
};
|
|
1993
2098
|
const handler = () => {
|