@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.mjs
CHANGED
|
@@ -57,53 +57,6 @@ function send(url) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// src/logger.ts
|
|
61
|
-
var LOG_LEVELS = ["silent", "error", "warn", "info", "debug"];
|
|
62
|
-
var RANK = {
|
|
63
|
-
silent: 0,
|
|
64
|
-
error: 1,
|
|
65
|
-
warn: 2,
|
|
66
|
-
info: 3,
|
|
67
|
-
debug: 4
|
|
68
|
-
};
|
|
69
|
-
var currentLevel = "warn";
|
|
70
|
-
function setLogLevel(level) {
|
|
71
|
-
if (typeof level === "string" && Object.prototype.hasOwnProperty.call(RANK, level)) {
|
|
72
|
-
currentLevel = level;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function write(method, args) {
|
|
76
|
-
try {
|
|
77
|
-
const c = globalThis.console;
|
|
78
|
-
if (!c) return;
|
|
79
|
-
const fn = c[method] ?? c.log;
|
|
80
|
-
if (typeof fn === "function") fn.call(c, ...args);
|
|
81
|
-
} catch {
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
var logger = {
|
|
85
|
-
error(...args) {
|
|
86
|
-
if (RANK[currentLevel] >= RANK.error) write("error", args);
|
|
87
|
-
},
|
|
88
|
-
warn(...args) {
|
|
89
|
-
if (RANK[currentLevel] >= RANK.warn) write("warn", args);
|
|
90
|
-
},
|
|
91
|
-
info(...args) {
|
|
92
|
-
if (RANK[currentLevel] >= RANK.info) write("info", args);
|
|
93
|
-
},
|
|
94
|
-
debug(...args) {
|
|
95
|
-
if (RANK[currentLevel] >= RANK.debug) write("debug", args);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
function safeRun(label, fallback, fn) {
|
|
99
|
-
try {
|
|
100
|
-
return fn();
|
|
101
|
-
} catch (err) {
|
|
102
|
-
logger.error(`[shipeasy] ${label} failed \u2014 returning safe default:`, String(err));
|
|
103
|
-
return fallback;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
60
|
// src/see/core.ts
|
|
108
61
|
var SEE_MAX_MESSAGE = 500;
|
|
109
62
|
var SEE_MAX_STACK = 8e3;
|
|
@@ -221,7 +174,7 @@ function captureCallsiteStack() {
|
|
|
221
174
|
const kept = lines.slice(1).filter((l) => !/@shipeasy[\\/]sdk|see[\\/]core|captureCallsiteStack|\bsee\b\s*\(/.test(l));
|
|
222
175
|
return kept.length ? kept.join("\n") : void 0;
|
|
223
176
|
}
|
|
224
|
-
function buildSeeEvent(problem, consequence, extras,
|
|
177
|
+
function buildSeeEvent(problem, consequence, extras, ctx2, kindOverride, correlationId) {
|
|
225
178
|
let errorType;
|
|
226
179
|
let message;
|
|
227
180
|
let stack;
|
|
@@ -249,8 +202,8 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlat
|
|
|
249
202
|
message: truncate(message, SEE_MAX_MESSAGE),
|
|
250
203
|
subject: consequence.subject,
|
|
251
204
|
outcome: consequence.outcome,
|
|
252
|
-
side:
|
|
253
|
-
sdk_version:
|
|
205
|
+
side: ctx2.side,
|
|
206
|
+
sdk_version: ctx2.sdkVersion,
|
|
254
207
|
ts: Date.now()
|
|
255
208
|
};
|
|
256
209
|
if (stack) ev.stack = truncate(stack, SEE_MAX_STACK);
|
|
@@ -259,10 +212,10 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlat
|
|
|
259
212
|
if (causedBy) ev.caused_by = causedBy;
|
|
260
213
|
const cleanExtras = sanitizeExtras(extras);
|
|
261
214
|
if (cleanExtras) ev.extras = cleanExtras;
|
|
262
|
-
if (
|
|
263
|
-
if (
|
|
264
|
-
if (
|
|
265
|
-
if (
|
|
215
|
+
if (ctx2.url) ev.url = truncate(ctx2.url, SEE_MAX_SUBJECT);
|
|
216
|
+
if (ctx2.userId) ev.user_id = ctx2.userId;
|
|
217
|
+
if (ctx2.anonId) ev.anonymous_id = ctx2.anonId;
|
|
218
|
+
if (ctx2.env) ev.env = ctx2.env;
|
|
266
219
|
markReported(problem, ev);
|
|
267
220
|
return ev;
|
|
268
221
|
}
|
|
@@ -355,6 +308,98 @@ var SeeLimiter = class {
|
|
|
355
308
|
}
|
|
356
309
|
};
|
|
357
310
|
|
|
311
|
+
// src/internal-report.ts
|
|
312
|
+
var INGEST_URL = "https://api.shipeasy.ai/collect";
|
|
313
|
+
var PLACEHOLDER_KEY = "sdk_client_REPLACE_WITH_SHIPEASY_INTERNAL_ERROR_KEY";
|
|
314
|
+
var INGEST_KEY = "sdk_client_00bd4608a03e4084922978f9522614d5";
|
|
315
|
+
function keyConfigured() {
|
|
316
|
+
return !!INGEST_KEY && INGEST_KEY !== PLACEHOLDER_KEY;
|
|
317
|
+
}
|
|
318
|
+
var OUTCOME = "returned a safe default";
|
|
319
|
+
var SDK_ID = "ts";
|
|
320
|
+
var ctx = null;
|
|
321
|
+
var limiter = new SeeLimiter();
|
|
322
|
+
function setInternalReportContext(c) {
|
|
323
|
+
ctx = { side: c.side, sdkVersion: c.sdkVersion, enabled: c.enabled !== false };
|
|
324
|
+
}
|
|
325
|
+
function reportInternalError(label, err) {
|
|
326
|
+
try {
|
|
327
|
+
if (!ctx || !ctx.enabled || !keyConfigured()) return;
|
|
328
|
+
const ev = buildSeeEvent(
|
|
329
|
+
err,
|
|
330
|
+
causesThe(label).to(OUTCOME),
|
|
331
|
+
{ sdk: SDK_ID },
|
|
332
|
+
{ side: ctx.side, sdkVersion: ctx.sdkVersion },
|
|
333
|
+
"caught"
|
|
334
|
+
);
|
|
335
|
+
if (!limiter.shouldSend(ev)) return;
|
|
336
|
+
send2(INGEST_URL, INGEST_KEY, JSON.stringify({ events: [ev] }));
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function send2(url, key, body) {
|
|
341
|
+
try {
|
|
342
|
+
const f = globalThis.fetch;
|
|
343
|
+
if (typeof f !== "function") return;
|
|
344
|
+
void f(url, {
|
|
345
|
+
method: "POST",
|
|
346
|
+
headers: { "X-SDK-Key": key, "Content-Type": "text/plain" },
|
|
347
|
+
body,
|
|
348
|
+
keepalive: true
|
|
349
|
+
}).catch(() => {
|
|
350
|
+
});
|
|
351
|
+
} catch {
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/logger.ts
|
|
356
|
+
var LOG_LEVELS = ["silent", "error", "warn", "info", "debug"];
|
|
357
|
+
var RANK = {
|
|
358
|
+
silent: 0,
|
|
359
|
+
error: 1,
|
|
360
|
+
warn: 2,
|
|
361
|
+
info: 3,
|
|
362
|
+
debug: 4
|
|
363
|
+
};
|
|
364
|
+
var currentLevel = "warn";
|
|
365
|
+
function setLogLevel(level) {
|
|
366
|
+
if (typeof level === "string" && Object.prototype.hasOwnProperty.call(RANK, level)) {
|
|
367
|
+
currentLevel = level;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function write(method, args) {
|
|
371
|
+
try {
|
|
372
|
+
const c = globalThis.console;
|
|
373
|
+
if (!c) return;
|
|
374
|
+
const fn = c[method] ?? c.log;
|
|
375
|
+
if (typeof fn === "function") fn.call(c, ...args);
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
var logger = {
|
|
380
|
+
error(...args) {
|
|
381
|
+
if (RANK[currentLevel] >= RANK.error) write("error", args);
|
|
382
|
+
},
|
|
383
|
+
warn(...args) {
|
|
384
|
+
if (RANK[currentLevel] >= RANK.warn) write("warn", args);
|
|
385
|
+
},
|
|
386
|
+
info(...args) {
|
|
387
|
+
if (RANK[currentLevel] >= RANK.info) write("info", args);
|
|
388
|
+
},
|
|
389
|
+
debug(...args) {
|
|
390
|
+
if (RANK[currentLevel] >= RANK.debug) write("debug", args);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
function safeRun(label, fallback, fn) {
|
|
394
|
+
try {
|
|
395
|
+
return fn();
|
|
396
|
+
} catch (err) {
|
|
397
|
+
logger.error(`[shipeasy] ${label} failed \u2014 returning safe default:`, String(err));
|
|
398
|
+
reportInternalError(label, err);
|
|
399
|
+
return fallback;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
358
403
|
// src/client/index.ts
|
|
359
404
|
var version = "4.0.0";
|
|
360
405
|
var FLAG_REASONS = [
|
|
@@ -365,6 +410,40 @@ var FLAG_REASONS = [
|
|
|
365
410
|
"RULE_MATCH",
|
|
366
411
|
"DEFAULT"
|
|
367
412
|
];
|
|
413
|
+
var AssignmentImpl = class {
|
|
414
|
+
constructor(name, group, params) {
|
|
415
|
+
this.name = name;
|
|
416
|
+
this.group = group;
|
|
417
|
+
this.params = params;
|
|
418
|
+
}
|
|
419
|
+
name;
|
|
420
|
+
group;
|
|
421
|
+
params;
|
|
422
|
+
get enrolled() {
|
|
423
|
+
return this.group !== null;
|
|
424
|
+
}
|
|
425
|
+
get(field, fallback) {
|
|
426
|
+
const v = this.params[field];
|
|
427
|
+
return v === void 0 ? fallback : v;
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
function hasDomEvents() {
|
|
431
|
+
return typeof window !== "undefined" && typeof window.addEventListener === "function";
|
|
432
|
+
}
|
|
433
|
+
function onWindow(type, handler, opts) {
|
|
434
|
+
try {
|
|
435
|
+
if (hasDomEvents()) window.addEventListener(type, handler, opts);
|
|
436
|
+
} catch {
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function onDocument(type, handler, opts) {
|
|
440
|
+
try {
|
|
441
|
+
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
|
442
|
+
document.addEventListener(type, handler, opts);
|
|
443
|
+
}
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
}
|
|
368
447
|
var FLUSH_INTERVAL_MS = 5e3;
|
|
369
448
|
var MAX_BUFFER = 100;
|
|
370
449
|
var ANON_ID_KEY = "__se_anon_id";
|
|
@@ -376,8 +455,8 @@ var EventBuffer = class {
|
|
|
376
455
|
this.sdkKey = sdkKey;
|
|
377
456
|
if (typeof window !== "undefined") {
|
|
378
457
|
this.timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
379
|
-
|
|
380
|
-
|
|
458
|
+
onWindow("beforeunload", () => this.flush());
|
|
459
|
+
onDocument("visibilitychange", () => {
|
|
381
460
|
if (document.visibilityState === "hidden") this.flush(true);
|
|
382
461
|
});
|
|
383
462
|
try {
|
|
@@ -922,6 +1001,10 @@ var Engine = class _Engine {
|
|
|
922
1001
|
env;
|
|
923
1002
|
evalResult = null;
|
|
924
1003
|
anonId;
|
|
1004
|
+
// Resolves once a caller-supplied anonymousStore has been consulted (persisted
|
|
1005
|
+
// id adopted, or the minted id written back). identify() awaits it so the
|
|
1006
|
+
// stable id is settled before the first /sdk/evaluate. null when no store.
|
|
1007
|
+
anonReady = null;
|
|
925
1008
|
userId = "";
|
|
926
1009
|
buffer;
|
|
927
1010
|
telemetry;
|
|
@@ -949,9 +1032,14 @@ var Engine = class _Engine {
|
|
|
949
1032
|
constructor(opts) {
|
|
950
1033
|
setLogLevel(opts.logLevel);
|
|
951
1034
|
this.sdkKey = opts.sdkKey;
|
|
952
|
-
this.baseUrl = (opts.baseUrl ?? "https://
|
|
1035
|
+
this.baseUrl = (opts.baseUrl ?? "https://api.shipeasy.ai").replace(/\/$/, "");
|
|
953
1036
|
this.env = opts.env ?? "prod";
|
|
954
1037
|
this.testMode = opts.testMode === true;
|
|
1038
|
+
setInternalReportContext({
|
|
1039
|
+
side: "client",
|
|
1040
|
+
sdkVersion: version,
|
|
1041
|
+
enabled: !this.testMode && opts.disableInternalErrorReporting !== true
|
|
1042
|
+
});
|
|
955
1043
|
this.autoGuardrails = opts.autoGuardrails !== false;
|
|
956
1044
|
this.autoCollectAlways = opts.autoCollectAlways === true;
|
|
957
1045
|
this.disableAutoExposure = opts.disableAutoExposure === true;
|
|
@@ -964,6 +1052,9 @@ var Engine = class _Engine {
|
|
|
964
1052
|
engagement: g.engagement ?? this.autoGuardrails
|
|
965
1053
|
};
|
|
966
1054
|
this.anonId = getOrCreateAnonId();
|
|
1055
|
+
if (opts.anonymousStore && !this.testMode) {
|
|
1056
|
+
this.anonReady = this.hydrateAnonFromStore(opts.anonymousStore);
|
|
1057
|
+
}
|
|
967
1058
|
this.buffer = new EventBuffer(`${this.baseUrl}/collect`, this.sdkKey);
|
|
968
1059
|
this.telemetry = new Telemetry({
|
|
969
1060
|
endpoint: opts.telemetryUrl ?? DEFAULT_TELEMETRY_URL,
|
|
@@ -979,6 +1070,23 @@ var Engine = class _Engine {
|
|
|
979
1070
|
void this.buffer.flushPendingAlias();
|
|
980
1071
|
}
|
|
981
1072
|
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Consult the caller-supplied {@link AnonymousStore}: adopt a persisted id so
|
|
1075
|
+
* the visitor buckets identically across app launches, or persist the id just
|
|
1076
|
+
* minted by getOrCreateAnonId() on a fresh device. Best-effort — any store
|
|
1077
|
+
* failure leaves the in-memory id in place.
|
|
1078
|
+
*/
|
|
1079
|
+
async hydrateAnonFromStore(store) {
|
|
1080
|
+
try {
|
|
1081
|
+
const stored = await store.get(ANON_ID_KEY);
|
|
1082
|
+
if (typeof stored === "string" && stored) {
|
|
1083
|
+
this.anonId = stored;
|
|
1084
|
+
} else {
|
|
1085
|
+
await store.set(ANON_ID_KEY, this.anonId);
|
|
1086
|
+
}
|
|
1087
|
+
} catch {
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
982
1090
|
/**
|
|
983
1091
|
* Build a no-network, immediately-usable browser client for tests
|
|
984
1092
|
* (Statsig-style). identify() is a no-op (never calls /sdk/evaluate), track()
|
|
@@ -1006,6 +1114,10 @@ var Engine = class _Engine {
|
|
|
1006
1114
|
this.notify();
|
|
1007
1115
|
return;
|
|
1008
1116
|
}
|
|
1117
|
+
if (this.anonReady) {
|
|
1118
|
+
await this.anonReady;
|
|
1119
|
+
this.anonReady = null;
|
|
1120
|
+
}
|
|
1009
1121
|
const seq = ++this.identifySeq;
|
|
1010
1122
|
const prevUserId = this.userId;
|
|
1011
1123
|
if (user.user_id !== void 0) this.userId = user.user_id;
|
|
@@ -1163,50 +1275,37 @@ var Engine = class _Engine {
|
|
|
1163
1275
|
return void 0;
|
|
1164
1276
|
}
|
|
1165
1277
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1278
|
+
/**
|
|
1279
|
+
* Assign the visitor within a universe: `universe("checkout").assign()`. A
|
|
1280
|
+
* universe is a mutual-exclusion pool, so the visitor lands in ≤1 experiment;
|
|
1281
|
+
* the returned {@link Assignment} exposes `.group` / `.get(field, fallback)`
|
|
1282
|
+
* and auto-logs one exposure when enrolled (unless `disableAutoExposure` or
|
|
1283
|
+
* `assign({ logExposure: false })`). An un-enrolled visitor still resolves
|
|
1284
|
+
* `get()` to the universe defaults. This is the sole experiment read path —
|
|
1285
|
+
* there is no `getExperiment` (ask a universe, not an experiment).
|
|
1286
|
+
*/
|
|
1287
|
+
universe(name) {
|
|
1288
|
+
return { assign: (opts) => this.assignUniverse(name, opts) };
|
|
1289
|
+
}
|
|
1290
|
+
assignUniverse(name, opts) {
|
|
1169
1291
|
this.telemetry.emit("experiment", name);
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
params
|
|
1174
|
-
};
|
|
1175
|
-
const pov = this.experimentOverrides.get(name);
|
|
1176
|
-
if (pov) {
|
|
1177
|
-
const variantParams = variants?.[pov.group];
|
|
1178
|
-
const params = { ...defaultParams, ...pov.params, ...variantParams ?? {} };
|
|
1179
|
-
return { inExperiment: true, group: pov.group, params };
|
|
1292
|
+
const defaults = this.evalResult?.universes?.[name]?.defaults ?? {};
|
|
1293
|
+
for (const [expName, pov] of this.experimentOverrides) {
|
|
1294
|
+
if (this.evalResult?.experiments[expName]?.universe !== name) continue;
|
|
1295
|
+
return new AssignmentImpl(expName, pov.group, { ...defaults, ...pov.params });
|
|
1180
1296
|
}
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
return { inExperiment: true, group: ov, params };
|
|
1297
|
+
for (const [expName, entry] of Object.entries(this.evalResult?.experiments ?? {})) {
|
|
1298
|
+
if (entry.universe !== name) continue;
|
|
1299
|
+
const ov = readExpOverride(expName);
|
|
1300
|
+
if (ov !== null) return new AssignmentImpl(expName, ov, { ...defaults, ...entry.params });
|
|
1186
1301
|
}
|
|
1187
|
-
const entry
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
try {
|
|
1193
|
-
return { inExperiment: true, group: entry.group, params: decode(entry.params) };
|
|
1194
|
-
} catch (err) {
|
|
1195
|
-
logger.warn(`[shipeasy] getExperiment('${name}') decode failed:`, String(err));
|
|
1196
|
-
return notIn;
|
|
1302
|
+
for (const [expName, entry] of Object.entries(this.evalResult?.experiments ?? {})) {
|
|
1303
|
+
if (entry.universe !== name || !entry.inExperiment) continue;
|
|
1304
|
+
const shouldLog = opts?.logExposure ?? !this.disableAutoExposure;
|
|
1305
|
+
if (shouldLog) this.buffer.pushExposure(expName, entry.group, this.userId, this.anonId);
|
|
1306
|
+
return new AssignmentImpl(expName, entry.group, entry.params);
|
|
1197
1307
|
}
|
|
1198
|
-
|
|
1199
|
-
/**
|
|
1200
|
-
* Manually log an exposure for an enrolled experiment (Statsig's
|
|
1201
|
-
* `manuallyLogExposure`). Reads the cached eval result; if the visitor is in
|
|
1202
|
-
* the experiment, pushes the session-deduped exposure. Pair this with the
|
|
1203
|
-
* render of the treatment when reading with `{ logExposure: false }` (or
|
|
1204
|
-
* `disableAutoExposure: true`). No-op if the visitor isn't enrolled.
|
|
1205
|
-
*/
|
|
1206
|
-
logExposure(name) {
|
|
1207
|
-
const entry = this.evalResult?.experiments[name];
|
|
1208
|
-
if (!entry || !entry.inExperiment) return;
|
|
1209
|
-
this.buffer.pushExposure(name, entry.group, this.userId, this.anonId);
|
|
1308
|
+
return new AssignmentImpl(null, null, defaults);
|
|
1210
1309
|
}
|
|
1211
1310
|
/**
|
|
1212
1311
|
* Subscribe to state changes — fires after identify() completes and on
|
|
@@ -1215,7 +1314,7 @@ var Engine = class _Engine {
|
|
|
1215
1314
|
*/
|
|
1216
1315
|
subscribe(listener) {
|
|
1217
1316
|
this.listeners.add(listener);
|
|
1218
|
-
if (!this.overrideListenerInstalled &&
|
|
1317
|
+
if (!this.overrideListenerInstalled && hasDomEvents()) {
|
|
1219
1318
|
this.overrideListenerInstalled = true;
|
|
1220
1319
|
window.addEventListener("se:override:change", this.onOverrideChange);
|
|
1221
1320
|
}
|
|
@@ -1231,9 +1330,11 @@ var Engine = class _Engine {
|
|
|
1231
1330
|
if (typeof window === "undefined") return null;
|
|
1232
1331
|
const bridge = {
|
|
1233
1332
|
getFlag: (n) => this.getFlag(n),
|
|
1333
|
+
// Devtools reads assignments by experiment name straight off the eval
|
|
1334
|
+
// result (the public read path is universe-first; this is display-only).
|
|
1234
1335
|
getExperiment: (n) => {
|
|
1235
|
-
const
|
|
1236
|
-
return { inExperiment:
|
|
1336
|
+
const entry = this.evalResult?.experiments[n];
|
|
1337
|
+
return { inExperiment: entry?.inExperiment ?? false, group: entry?.group ?? "control" };
|
|
1237
1338
|
},
|
|
1238
1339
|
getConfig: (n) => this.getConfig(n)
|
|
1239
1340
|
};
|
|
@@ -1356,7 +1457,7 @@ function loadDevtools(opts = {}) {
|
|
|
1356
1457
|
}
|
|
1357
1458
|
}
|
|
1358
1459
|
function attachDevtools(client, opts = {}) {
|
|
1359
|
-
if (
|
|
1460
|
+
if (!hasDomEvents()) return () => {
|
|
1360
1461
|
};
|
|
1361
1462
|
const hotkey = opts.hotkey ?? "Shift+Alt+S";
|
|
1362
1463
|
const parts = hotkey.split("+");
|
|
@@ -1400,9 +1501,11 @@ function shipeasy(opts) {
|
|
|
1400
1501
|
autoGuardrailGroups: groups,
|
|
1401
1502
|
autoCollectAlways: acObj?.always === true,
|
|
1402
1503
|
disableTelemetry: opts.disableTelemetry,
|
|
1504
|
+
disableInternalErrorReporting: opts.disableInternalErrorReporting,
|
|
1403
1505
|
disableAutoExposure: opts.disableAutoExposure,
|
|
1404
1506
|
privateAttributes: opts.privateAttributes,
|
|
1405
|
-
stickyBucketing: opts.stickyBucketing
|
|
1507
|
+
stickyBucketing: opts.stickyBucketing,
|
|
1508
|
+
anonymousStore: opts.anonymousStore
|
|
1406
1509
|
});
|
|
1407
1510
|
injectI18nLoader(opts.clientKey, baseUrl, opts.i18nProfile);
|
|
1408
1511
|
flags.notifyMounted();
|
|
@@ -1484,7 +1587,7 @@ var _mountedAndReady = false;
|
|
|
1484
1587
|
var _standaloneListeners = /* @__PURE__ */ new Set();
|
|
1485
1588
|
var _standaloneOverrideWired = false;
|
|
1486
1589
|
function wireStandaloneOverride() {
|
|
1487
|
-
if (_standaloneOverrideWired ||
|
|
1590
|
+
if (_standaloneOverrideWired || !hasDomEvents()) return;
|
|
1488
1591
|
_standaloneOverrideWired = true;
|
|
1489
1592
|
window.addEventListener("se:override:change", () => {
|
|
1490
1593
|
for (const cb of _standaloneListeners) cb();
|
|
@@ -1550,21 +1653,22 @@ var flags = {
|
|
|
1550
1653
|
}
|
|
1551
1654
|
});
|
|
1552
1655
|
},
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1656
|
+
/**
|
|
1657
|
+
* Assign the visitor within a universe: `flags.universe("checkout").assign()`.
|
|
1658
|
+
* A universe is a mutual-exclusion pool, so the visitor lands in ≤1 experiment;
|
|
1659
|
+
* the returned {@link Assignment} exposes `.group` / `.get(field, fallback)`
|
|
1660
|
+
* and auto-logs one exposure when enrolled. Before configure() (or on error)
|
|
1661
|
+
* returns a safe not-enrolled handle. Replaces the removed `getExperiment` —
|
|
1662
|
+
* read experiments by universe, never by name.
|
|
1663
|
+
*/
|
|
1664
|
+
universe(name) {
|
|
1665
|
+
return {
|
|
1666
|
+
assign: (opts) => safeRun(
|
|
1667
|
+
"flags.universe.assign",
|
|
1668
|
+
new AssignmentImpl(null, null, {}),
|
|
1669
|
+
() => _client ? _client.universe(name).assign(opts) : new AssignmentImpl(null, null, {})
|
|
1670
|
+
)
|
|
1558
1671
|
};
|
|
1559
|
-
return safeRun("flags.getExperiment", fallback, () => {
|
|
1560
|
-
if (!_client) return fallback;
|
|
1561
|
-
return typeof decodeOrOpts === "function" ? _client.getExperiment(name, defaultParams, decodeOrOpts, variants) : _client.getExperiment(name, defaultParams, decodeOrOpts ?? {});
|
|
1562
|
-
});
|
|
1563
|
-
},
|
|
1564
|
-
/** Manually log an exposure for an enrolled experiment. See
|
|
1565
|
-
* {@link Engine.logExposure}. No-op before configure(). */
|
|
1566
|
-
logExposure(name) {
|
|
1567
|
-
safeRun("flags.logExposure", void 0, () => _client?.logExposure(name));
|
|
1568
1672
|
},
|
|
1569
1673
|
track(eventName, props) {
|
|
1570
1674
|
safeRun("flags.track", void 0, () => _client?.track(eventName, props));
|
|
@@ -1670,13 +1774,20 @@ var Client = class {
|
|
|
1670
1774
|
() => this.engine.getConfig(name, decodeOrOpts)
|
|
1671
1775
|
);
|
|
1672
1776
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1777
|
+
/**
|
|
1778
|
+
* Assign the visitor within a universe: `client.universe("checkout").assign()`.
|
|
1779
|
+
* A universe is a mutual-exclusion pool, so the visitor lands in ≤1 experiment;
|
|
1780
|
+
* returns an {@link Assignment} (`.group` / `.get(field, fallback)`) and
|
|
1781
|
+
* auto-logs one exposure when enrolled. Replaces the removed `getExperiment`.
|
|
1782
|
+
*/
|
|
1783
|
+
universe(name) {
|
|
1784
|
+
return {
|
|
1785
|
+
assign: (opts) => safeRun(
|
|
1786
|
+
"Client.universe.assign",
|
|
1787
|
+
new AssignmentImpl(null, null, {}),
|
|
1788
|
+
() => this.engine.universe(name).assign(opts)
|
|
1789
|
+
)
|
|
1790
|
+
};
|
|
1680
1791
|
}
|
|
1681
1792
|
/** Read a killswitch (not user-bound; mirrors {@link Engine.getKillswitch}). */
|
|
1682
1793
|
getKillswitch(name, switchKey) {
|
|
@@ -1691,15 +1802,6 @@ var Client = class {
|
|
|
1691
1802
|
track(eventName, props) {
|
|
1692
1803
|
safeRun("Client.track", void 0, () => this.engine.track(eventName, props));
|
|
1693
1804
|
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Log an exposure for `name` at the treatment's render for the bound user.
|
|
1696
|
-
* Delegates to {@link Engine.logExposure} (no-op when the visitor isn't
|
|
1697
|
-
* enrolled). Pair with `getExperiment(name, …, { logExposure: false })` /
|
|
1698
|
-
* `disableAutoExposure` to log exposure exactly when you render.
|
|
1699
|
-
*/
|
|
1700
|
-
logExposure(name) {
|
|
1701
|
-
safeRun("Client.logExposure", void 0, () => this.engine.logExposure(name));
|
|
1702
|
-
}
|
|
1703
1805
|
};
|
|
1704
1806
|
function dispatchSee(problem, consequence, extras, kind) {
|
|
1705
1807
|
if (!_client) {
|
|
@@ -1925,6 +2027,7 @@ var i18n = {
|
|
|
1925
2027
|
whenReady() {
|
|
1926
2028
|
if (typeof window === "undefined") return Promise.resolve();
|
|
1927
2029
|
if (window.i18n?.locale) return Promise.resolve();
|
|
2030
|
+
if (!hasDomEvents()) return Promise.resolve();
|
|
1928
2031
|
return new Promise((resolve) => {
|
|
1929
2032
|
const handler = () => resolve();
|
|
1930
2033
|
window.addEventListener("se:i18n:ready", handler, { once: true });
|
|
@@ -1935,6 +2038,8 @@ var i18n = {
|
|
|
1935
2038
|
if (typeof window === "undefined") return () => {
|
|
1936
2039
|
};
|
|
1937
2040
|
if (window.i18n) return window.i18n.on("update", cb);
|
|
2041
|
+
if (!hasDomEvents()) return () => {
|
|
2042
|
+
};
|
|
1938
2043
|
let unsub = () => {
|
|
1939
2044
|
};
|
|
1940
2045
|
const handler = () => {
|