@shipeasy/sdk 6.0.0 → 6.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/README.md +78 -351
- package/dist/client/index.d.mts +34 -1
- package/dist/client/index.d.ts +34 -1
- package/dist/client/index.js +142 -50
- package/dist/client/index.mjs +141 -50
- package/dist/openfeature-server/index.d.mts +27 -2
- package/dist/openfeature-server/index.d.ts +27 -2
- package/dist/openfeature-server/index.js +227 -4
- package/dist/openfeature-server/index.mjs +227 -4
- package/dist/openfeature-web/index.d.mts +10 -0
- package/dist/openfeature-web/index.d.ts +10 -0
- package/dist/server/index.d.mts +122 -1
- package/dist/server/index.d.ts +122 -1
- package/dist/server/index.js +214 -37
- package/dist/server/index.mjs +206 -37
- package/dist/skill-cli.js +56 -0
- package/docs/skill/SKILL.md +145 -0
- package/package.json +13 -6
package/dist/client/index.js
CHANGED
|
@@ -27,6 +27,7 @@ __export(client_exports, {
|
|
|
27
27
|
LABEL_MARKER_RE: () => LABEL_MARKER_RE,
|
|
28
28
|
LABEL_MARKER_SEP: () => LABEL_MARKER_SEP,
|
|
29
29
|
LABEL_MARKER_START: () => LABEL_MARKER_START,
|
|
30
|
+
LOG_LEVELS: () => LOG_LEVELS,
|
|
30
31
|
_resetConfigureForTests: () => _resetConfigureForTests,
|
|
31
32
|
_resetShipeasyForTests: () => _resetShipeasyForTests,
|
|
32
33
|
attachDevtools: () => attachDevtools,
|
|
@@ -109,6 +110,53 @@ function send(url) {
|
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
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
|
+
|
|
112
160
|
// src/see/core.ts
|
|
113
161
|
var SEE_MAX_MESSAGE = 500;
|
|
114
162
|
var SEE_MAX_STACK = 8e3;
|
|
@@ -952,6 +1000,7 @@ var Engine = class _Engine {
|
|
|
952
1000
|
this.notify();
|
|
953
1001
|
};
|
|
954
1002
|
constructor(opts) {
|
|
1003
|
+
setLogLevel(opts.logLevel);
|
|
955
1004
|
this.sdkKey = opts.sdkKey;
|
|
956
1005
|
this.baseUrl = (opts.baseUrl ?? "https://edge.shipeasy.dev").replace(/\/$/, "");
|
|
957
1006
|
this.env = opts.env ?? "prod";
|
|
@@ -1086,7 +1135,7 @@ var Engine = class _Engine {
|
|
|
1086
1135
|
try {
|
|
1087
1136
|
l();
|
|
1088
1137
|
} catch (err) {
|
|
1089
|
-
|
|
1138
|
+
logger.warn("[shipeasy] subscriber threw:", String(err));
|
|
1090
1139
|
}
|
|
1091
1140
|
}
|
|
1092
1141
|
}
|
|
@@ -1163,7 +1212,7 @@ var Engine = class _Engine {
|
|
|
1163
1212
|
try {
|
|
1164
1213
|
return opts.decode(raw);
|
|
1165
1214
|
} catch (err) {
|
|
1166
|
-
|
|
1215
|
+
logger.warn(`[shipeasy] getConfig('${name}') decode failed:`, String(err));
|
|
1167
1216
|
return void 0;
|
|
1168
1217
|
}
|
|
1169
1218
|
}
|
|
@@ -1196,7 +1245,7 @@ var Engine = class _Engine {
|
|
|
1196
1245
|
try {
|
|
1197
1246
|
return { inExperiment: true, group: entry.group, params: decode(entry.params) };
|
|
1198
1247
|
} catch (err) {
|
|
1199
|
-
|
|
1248
|
+
logger.warn(`[shipeasy] getExperiment('${name}') decode failed:`, String(err));
|
|
1200
1249
|
return notIn;
|
|
1201
1250
|
}
|
|
1202
1251
|
}
|
|
@@ -1399,6 +1448,7 @@ function shipeasy(opts) {
|
|
|
1399
1448
|
const client = configureShipeasy({
|
|
1400
1449
|
sdkKey: opts.clientKey,
|
|
1401
1450
|
baseUrl,
|
|
1451
|
+
logLevel: opts.logLevel,
|
|
1402
1452
|
autoGuardrails: blanket,
|
|
1403
1453
|
autoGuardrailGroups: groups,
|
|
1404
1454
|
autoCollectAlways: acObj?.always === true,
|
|
@@ -1411,7 +1461,7 @@ function shipeasy(opts) {
|
|
|
1411
1461
|
flags.notifyMounted();
|
|
1412
1462
|
if (opts.autoIdentify !== false) {
|
|
1413
1463
|
void client.identify({}).catch((err) => {
|
|
1414
|
-
|
|
1464
|
+
logger.warn("[shipeasy] auto-identify failed:", String(err));
|
|
1415
1465
|
});
|
|
1416
1466
|
}
|
|
1417
1467
|
return attachDevtools(client, { adminUrl: opts.adminUrl });
|
|
@@ -1499,7 +1549,7 @@ var flags = {
|
|
|
1499
1549
|
},
|
|
1500
1550
|
identify(user) {
|
|
1501
1551
|
if (!_client) {
|
|
1502
|
-
|
|
1552
|
+
logger.warn("[shipeasy] flags.identify called before configureShipeasy()");
|
|
1503
1553
|
return Promise.resolve();
|
|
1504
1554
|
}
|
|
1505
1555
|
return _client.identify(user);
|
|
@@ -1512,40 +1562,46 @@ var flags = {
|
|
|
1512
1562
|
* force-static pages where SSR has no flag data.
|
|
1513
1563
|
*/
|
|
1514
1564
|
get(name, defaultValue = false) {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1565
|
+
return safeRun("flags.get", defaultValue, () => {
|
|
1566
|
+
const bs = getBootstrap();
|
|
1567
|
+
if (bs !== null && name in bs.flags) return bs.flags[name];
|
|
1568
|
+
if (!_mountedAndReady) return defaultValue;
|
|
1569
|
+
if (_client) return _client.getFlag(name, defaultValue);
|
|
1570
|
+
return readGateOverride(name) ?? defaultValue;
|
|
1571
|
+
});
|
|
1520
1572
|
},
|
|
1521
1573
|
/** Evaluate a gate and report why (value + reason). See {@link FlagDetail}. */
|
|
1522
1574
|
getDetail(name) {
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1575
|
+
return safeRun("flags.getDetail", { value: false, reason: "CLIENT_NOT_READY" }, () => {
|
|
1576
|
+
if (_client) return _client.getFlagDetail(name);
|
|
1577
|
+
const ov = readGateOverride(name);
|
|
1578
|
+
if (ov !== null) return { value: ov, reason: "OVERRIDE" };
|
|
1579
|
+
return { value: false, reason: "CLIENT_NOT_READY" };
|
|
1580
|
+
});
|
|
1527
1581
|
},
|
|
1528
1582
|
getConfig(name, decode) {
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1583
|
+
return safeRun("flags.getConfig", void 0, () => {
|
|
1584
|
+
const bs = getBootstrap();
|
|
1585
|
+
if (bs !== null && name in bs.configs) {
|
|
1586
|
+
const raw = bs.configs[name];
|
|
1587
|
+
if (!decode) return raw;
|
|
1588
|
+
try {
|
|
1589
|
+
return decode(raw);
|
|
1590
|
+
} catch {
|
|
1591
|
+
return void 0;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
if (!_mountedAndReady) return void 0;
|
|
1595
|
+
if (_client) return _client.getConfig(name, decode);
|
|
1596
|
+
const ov = readConfigOverride(name);
|
|
1597
|
+
if (ov === void 0) return void 0;
|
|
1598
|
+
if (!decode) return ov;
|
|
1533
1599
|
try {
|
|
1534
|
-
return decode(
|
|
1600
|
+
return decode(ov);
|
|
1535
1601
|
} catch {
|
|
1536
1602
|
return void 0;
|
|
1537
1603
|
}
|
|
1538
|
-
}
|
|
1539
|
-
if (!_mountedAndReady) return void 0;
|
|
1540
|
-
if (_client) return _client.getConfig(name, decode);
|
|
1541
|
-
const ov = readConfigOverride(name);
|
|
1542
|
-
if (ov === void 0) return void 0;
|
|
1543
|
-
if (!decode) return ov;
|
|
1544
|
-
try {
|
|
1545
|
-
return decode(ov);
|
|
1546
|
-
} catch {
|
|
1547
|
-
return void 0;
|
|
1548
|
-
}
|
|
1604
|
+
});
|
|
1549
1605
|
},
|
|
1550
1606
|
getExperiment(name, defaultParams, decodeOrOpts, variants) {
|
|
1551
1607
|
const fallback = {
|
|
@@ -1553,16 +1609,18 @@ var flags = {
|
|
|
1553
1609
|
group: "control",
|
|
1554
1610
|
params: defaultParams
|
|
1555
1611
|
};
|
|
1556
|
-
|
|
1557
|
-
|
|
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
|
+
});
|
|
1558
1616
|
},
|
|
1559
1617
|
/** Manually log an exposure for an enrolled experiment. See
|
|
1560
1618
|
* {@link Engine.logExposure}. No-op before configure(). */
|
|
1561
1619
|
logExposure(name) {
|
|
1562
|
-
_client?.logExposure(name);
|
|
1620
|
+
safeRun("flags.logExposure", void 0, () => _client?.logExposure(name));
|
|
1563
1621
|
},
|
|
1564
1622
|
track(eventName, props) {
|
|
1565
|
-
_client?.track(eventName, props);
|
|
1623
|
+
safeRun("flags.track", void 0, () => _client?.track(eventName, props));
|
|
1566
1624
|
},
|
|
1567
1625
|
/**
|
|
1568
1626
|
* Read a killswitch. Without `switchKey`, returns true when the killswitch is
|
|
@@ -1574,15 +1632,17 @@ var flags = {
|
|
|
1574
1632
|
* available synchronously on first render.
|
|
1575
1633
|
*/
|
|
1576
1634
|
ks(name, switchKey) {
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1635
|
+
return safeRun("flags.ks", false, () => {
|
|
1636
|
+
const bs = getBootstrap();
|
|
1637
|
+
if (bs !== null && bs.killswitches && name in bs.killswitches) {
|
|
1638
|
+
const ks = bs.killswitches[name];
|
|
1639
|
+
if (typeof ks === "boolean") return switchKey === void 0 ? ks : false;
|
|
1640
|
+
if (switchKey === void 0) return false;
|
|
1641
|
+
return ks[switchKey] === true;
|
|
1642
|
+
}
|
|
1643
|
+
if (!_mountedAndReady) return false;
|
|
1644
|
+
return _client?.getKillswitch(name, switchKey) ?? false;
|
|
1645
|
+
});
|
|
1586
1646
|
},
|
|
1587
1647
|
flush() {
|
|
1588
1648
|
return _client?.flush() ?? Promise.resolve();
|
|
@@ -1639,7 +1699,7 @@ var Client = class {
|
|
|
1639
1699
|
this.engine = engine;
|
|
1640
1700
|
this.attributes = _attributes(user);
|
|
1641
1701
|
this._identify = this.engine.identify(this.attributes).catch((err) => {
|
|
1642
|
-
|
|
1702
|
+
logger.warn("[shipeasy] Client identify failed:", String(err));
|
|
1643
1703
|
});
|
|
1644
1704
|
}
|
|
1645
1705
|
/** Resolves once the engine's identify() for this user has completed. */
|
|
@@ -1647,25 +1707,56 @@ var Client = class {
|
|
|
1647
1707
|
return this._identify;
|
|
1648
1708
|
}
|
|
1649
1709
|
getFlag(name, defaultValue = false) {
|
|
1650
|
-
return this.engine.getFlag(name, defaultValue);
|
|
1710
|
+
return safeRun("Client.getFlag", defaultValue, () => this.engine.getFlag(name, defaultValue));
|
|
1651
1711
|
}
|
|
1652
1712
|
getFlagDetail(name) {
|
|
1653
|
-
return
|
|
1713
|
+
return safeRun(
|
|
1714
|
+
"Client.getFlagDetail",
|
|
1715
|
+
{ value: false, reason: "CLIENT_NOT_READY" },
|
|
1716
|
+
() => this.engine.getFlagDetail(name)
|
|
1717
|
+
);
|
|
1654
1718
|
}
|
|
1655
1719
|
getConfig(name, decodeOrOpts) {
|
|
1656
|
-
return
|
|
1720
|
+
return safeRun(
|
|
1721
|
+
"Client.getConfig",
|
|
1722
|
+
void 0,
|
|
1723
|
+
() => this.engine.getConfig(name, decodeOrOpts)
|
|
1724
|
+
);
|
|
1657
1725
|
}
|
|
1658
1726
|
getExperiment(name, defaultParams, decode) {
|
|
1659
|
-
|
|
1727
|
+
const notIn = { inExperiment: false, group: "control", params: defaultParams };
|
|
1728
|
+
return safeRun(
|
|
1729
|
+
"Client.getExperiment",
|
|
1730
|
+
notIn,
|
|
1731
|
+
() => this.engine.getExperiment(name, defaultParams, decode)
|
|
1732
|
+
);
|
|
1660
1733
|
}
|
|
1661
1734
|
/** Read a killswitch (not user-bound; mirrors {@link Engine.getKillswitch}). */
|
|
1662
1735
|
getKillswitch(name, switchKey) {
|
|
1663
|
-
return this.engine.getKillswitch(name, switchKey);
|
|
1736
|
+
return safeRun("Client.getKillswitch", false, () => this.engine.getKillswitch(name, switchKey));
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Record a conversion/metric event for the bound (identified) user. Delegates
|
|
1740
|
+
* to {@link Engine.track} — so an experiment is end-to-end Client-only (no need
|
|
1741
|
+
* to drop down to the Engine to log a conversion). Fire-and-forget; no-op in
|
|
1742
|
+
* test mode.
|
|
1743
|
+
*/
|
|
1744
|
+
track(eventName, props) {
|
|
1745
|
+
safeRun("Client.track", void 0, () => this.engine.track(eventName, props));
|
|
1746
|
+
}
|
|
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));
|
|
1664
1755
|
}
|
|
1665
1756
|
};
|
|
1666
1757
|
function dispatchSee(problem, consequence, extras, kind) {
|
|
1667
1758
|
if (!_client) {
|
|
1668
|
-
|
|
1759
|
+
logger.warn("[shipeasy] see() called before shipeasy({ clientKey }) \u2014 error dropped");
|
|
1669
1760
|
return;
|
|
1670
1761
|
}
|
|
1671
1762
|
_client.reportError(problem, consequence, extras, kind);
|
|
@@ -1918,6 +2009,7 @@ var i18n = {
|
|
|
1918
2009
|
LABEL_MARKER_RE,
|
|
1919
2010
|
LABEL_MARKER_SEP,
|
|
1920
2011
|
LABEL_MARKER_START,
|
|
2012
|
+
LOG_LEVELS,
|
|
1921
2013
|
_resetConfigureForTests,
|
|
1922
2014
|
_resetShipeasyForTests,
|
|
1923
2015
|
attachDevtools,
|
package/dist/client/index.mjs
CHANGED
|
@@ -57,6 +57,53 @@ 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
|
+
|
|
60
107
|
// src/see/core.ts
|
|
61
108
|
var SEE_MAX_MESSAGE = 500;
|
|
62
109
|
var SEE_MAX_STACK = 8e3;
|
|
@@ -900,6 +947,7 @@ var Engine = class _Engine {
|
|
|
900
947
|
this.notify();
|
|
901
948
|
};
|
|
902
949
|
constructor(opts) {
|
|
950
|
+
setLogLevel(opts.logLevel);
|
|
903
951
|
this.sdkKey = opts.sdkKey;
|
|
904
952
|
this.baseUrl = (opts.baseUrl ?? "https://edge.shipeasy.dev").replace(/\/$/, "");
|
|
905
953
|
this.env = opts.env ?? "prod";
|
|
@@ -1034,7 +1082,7 @@ var Engine = class _Engine {
|
|
|
1034
1082
|
try {
|
|
1035
1083
|
l();
|
|
1036
1084
|
} catch (err) {
|
|
1037
|
-
|
|
1085
|
+
logger.warn("[shipeasy] subscriber threw:", String(err));
|
|
1038
1086
|
}
|
|
1039
1087
|
}
|
|
1040
1088
|
}
|
|
@@ -1111,7 +1159,7 @@ var Engine = class _Engine {
|
|
|
1111
1159
|
try {
|
|
1112
1160
|
return opts.decode(raw);
|
|
1113
1161
|
} catch (err) {
|
|
1114
|
-
|
|
1162
|
+
logger.warn(`[shipeasy] getConfig('${name}') decode failed:`, String(err));
|
|
1115
1163
|
return void 0;
|
|
1116
1164
|
}
|
|
1117
1165
|
}
|
|
@@ -1144,7 +1192,7 @@ var Engine = class _Engine {
|
|
|
1144
1192
|
try {
|
|
1145
1193
|
return { inExperiment: true, group: entry.group, params: decode(entry.params) };
|
|
1146
1194
|
} catch (err) {
|
|
1147
|
-
|
|
1195
|
+
logger.warn(`[shipeasy] getExperiment('${name}') decode failed:`, String(err));
|
|
1148
1196
|
return notIn;
|
|
1149
1197
|
}
|
|
1150
1198
|
}
|
|
@@ -1347,6 +1395,7 @@ function shipeasy(opts) {
|
|
|
1347
1395
|
const client = configureShipeasy({
|
|
1348
1396
|
sdkKey: opts.clientKey,
|
|
1349
1397
|
baseUrl,
|
|
1398
|
+
logLevel: opts.logLevel,
|
|
1350
1399
|
autoGuardrails: blanket,
|
|
1351
1400
|
autoGuardrailGroups: groups,
|
|
1352
1401
|
autoCollectAlways: acObj?.always === true,
|
|
@@ -1359,7 +1408,7 @@ function shipeasy(opts) {
|
|
|
1359
1408
|
flags.notifyMounted();
|
|
1360
1409
|
if (opts.autoIdentify !== false) {
|
|
1361
1410
|
void client.identify({}).catch((err) => {
|
|
1362
|
-
|
|
1411
|
+
logger.warn("[shipeasy] auto-identify failed:", String(err));
|
|
1363
1412
|
});
|
|
1364
1413
|
}
|
|
1365
1414
|
return attachDevtools(client, { adminUrl: opts.adminUrl });
|
|
@@ -1447,7 +1496,7 @@ var flags = {
|
|
|
1447
1496
|
},
|
|
1448
1497
|
identify(user) {
|
|
1449
1498
|
if (!_client) {
|
|
1450
|
-
|
|
1499
|
+
logger.warn("[shipeasy] flags.identify called before configureShipeasy()");
|
|
1451
1500
|
return Promise.resolve();
|
|
1452
1501
|
}
|
|
1453
1502
|
return _client.identify(user);
|
|
@@ -1460,40 +1509,46 @@ var flags = {
|
|
|
1460
1509
|
* force-static pages where SSR has no flag data.
|
|
1461
1510
|
*/
|
|
1462
1511
|
get(name, defaultValue = false) {
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1512
|
+
return safeRun("flags.get", defaultValue, () => {
|
|
1513
|
+
const bs = getBootstrap();
|
|
1514
|
+
if (bs !== null && name in bs.flags) return bs.flags[name];
|
|
1515
|
+
if (!_mountedAndReady) return defaultValue;
|
|
1516
|
+
if (_client) return _client.getFlag(name, defaultValue);
|
|
1517
|
+
return readGateOverride(name) ?? defaultValue;
|
|
1518
|
+
});
|
|
1468
1519
|
},
|
|
1469
1520
|
/** Evaluate a gate and report why (value + reason). See {@link FlagDetail}. */
|
|
1470
1521
|
getDetail(name) {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1522
|
+
return safeRun("flags.getDetail", { value: false, reason: "CLIENT_NOT_READY" }, () => {
|
|
1523
|
+
if (_client) return _client.getFlagDetail(name);
|
|
1524
|
+
const ov = readGateOverride(name);
|
|
1525
|
+
if (ov !== null) return { value: ov, reason: "OVERRIDE" };
|
|
1526
|
+
return { value: false, reason: "CLIENT_NOT_READY" };
|
|
1527
|
+
});
|
|
1475
1528
|
},
|
|
1476
1529
|
getConfig(name, decode) {
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1530
|
+
return safeRun("flags.getConfig", void 0, () => {
|
|
1531
|
+
const bs = getBootstrap();
|
|
1532
|
+
if (bs !== null && name in bs.configs) {
|
|
1533
|
+
const raw = bs.configs[name];
|
|
1534
|
+
if (!decode) return raw;
|
|
1535
|
+
try {
|
|
1536
|
+
return decode(raw);
|
|
1537
|
+
} catch {
|
|
1538
|
+
return void 0;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
if (!_mountedAndReady) return void 0;
|
|
1542
|
+
if (_client) return _client.getConfig(name, decode);
|
|
1543
|
+
const ov = readConfigOverride(name);
|
|
1544
|
+
if (ov === void 0) return void 0;
|
|
1545
|
+
if (!decode) return ov;
|
|
1481
1546
|
try {
|
|
1482
|
-
return decode(
|
|
1547
|
+
return decode(ov);
|
|
1483
1548
|
} catch {
|
|
1484
1549
|
return void 0;
|
|
1485
1550
|
}
|
|
1486
|
-
}
|
|
1487
|
-
if (!_mountedAndReady) return void 0;
|
|
1488
|
-
if (_client) return _client.getConfig(name, decode);
|
|
1489
|
-
const ov = readConfigOverride(name);
|
|
1490
|
-
if (ov === void 0) return void 0;
|
|
1491
|
-
if (!decode) return ov;
|
|
1492
|
-
try {
|
|
1493
|
-
return decode(ov);
|
|
1494
|
-
} catch {
|
|
1495
|
-
return void 0;
|
|
1496
|
-
}
|
|
1551
|
+
});
|
|
1497
1552
|
},
|
|
1498
1553
|
getExperiment(name, defaultParams, decodeOrOpts, variants) {
|
|
1499
1554
|
const fallback = {
|
|
@@ -1501,16 +1556,18 @@ var flags = {
|
|
|
1501
1556
|
group: "control",
|
|
1502
1557
|
params: defaultParams
|
|
1503
1558
|
};
|
|
1504
|
-
|
|
1505
|
-
|
|
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
|
+
});
|
|
1506
1563
|
},
|
|
1507
1564
|
/** Manually log an exposure for an enrolled experiment. See
|
|
1508
1565
|
* {@link Engine.logExposure}. No-op before configure(). */
|
|
1509
1566
|
logExposure(name) {
|
|
1510
|
-
_client?.logExposure(name);
|
|
1567
|
+
safeRun("flags.logExposure", void 0, () => _client?.logExposure(name));
|
|
1511
1568
|
},
|
|
1512
1569
|
track(eventName, props) {
|
|
1513
|
-
_client?.track(eventName, props);
|
|
1570
|
+
safeRun("flags.track", void 0, () => _client?.track(eventName, props));
|
|
1514
1571
|
},
|
|
1515
1572
|
/**
|
|
1516
1573
|
* Read a killswitch. Without `switchKey`, returns true when the killswitch is
|
|
@@ -1522,15 +1579,17 @@ var flags = {
|
|
|
1522
1579
|
* available synchronously on first render.
|
|
1523
1580
|
*/
|
|
1524
1581
|
ks(name, switchKey) {
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1582
|
+
return safeRun("flags.ks", false, () => {
|
|
1583
|
+
const bs = getBootstrap();
|
|
1584
|
+
if (bs !== null && bs.killswitches && name in bs.killswitches) {
|
|
1585
|
+
const ks = bs.killswitches[name];
|
|
1586
|
+
if (typeof ks === "boolean") return switchKey === void 0 ? ks : false;
|
|
1587
|
+
if (switchKey === void 0) return false;
|
|
1588
|
+
return ks[switchKey] === true;
|
|
1589
|
+
}
|
|
1590
|
+
if (!_mountedAndReady) return false;
|
|
1591
|
+
return _client?.getKillswitch(name, switchKey) ?? false;
|
|
1592
|
+
});
|
|
1534
1593
|
},
|
|
1535
1594
|
flush() {
|
|
1536
1595
|
return _client?.flush() ?? Promise.resolve();
|
|
@@ -1587,7 +1646,7 @@ var Client = class {
|
|
|
1587
1646
|
this.engine = engine;
|
|
1588
1647
|
this.attributes = _attributes(user);
|
|
1589
1648
|
this._identify = this.engine.identify(this.attributes).catch((err) => {
|
|
1590
|
-
|
|
1649
|
+
logger.warn("[shipeasy] Client identify failed:", String(err));
|
|
1591
1650
|
});
|
|
1592
1651
|
}
|
|
1593
1652
|
/** Resolves once the engine's identify() for this user has completed. */
|
|
@@ -1595,25 +1654,56 @@ var Client = class {
|
|
|
1595
1654
|
return this._identify;
|
|
1596
1655
|
}
|
|
1597
1656
|
getFlag(name, defaultValue = false) {
|
|
1598
|
-
return this.engine.getFlag(name, defaultValue);
|
|
1657
|
+
return safeRun("Client.getFlag", defaultValue, () => this.engine.getFlag(name, defaultValue));
|
|
1599
1658
|
}
|
|
1600
1659
|
getFlagDetail(name) {
|
|
1601
|
-
return
|
|
1660
|
+
return safeRun(
|
|
1661
|
+
"Client.getFlagDetail",
|
|
1662
|
+
{ value: false, reason: "CLIENT_NOT_READY" },
|
|
1663
|
+
() => this.engine.getFlagDetail(name)
|
|
1664
|
+
);
|
|
1602
1665
|
}
|
|
1603
1666
|
getConfig(name, decodeOrOpts) {
|
|
1604
|
-
return
|
|
1667
|
+
return safeRun(
|
|
1668
|
+
"Client.getConfig",
|
|
1669
|
+
void 0,
|
|
1670
|
+
() => this.engine.getConfig(name, decodeOrOpts)
|
|
1671
|
+
);
|
|
1605
1672
|
}
|
|
1606
1673
|
getExperiment(name, defaultParams, decode) {
|
|
1607
|
-
|
|
1674
|
+
const notIn = { inExperiment: false, group: "control", params: defaultParams };
|
|
1675
|
+
return safeRun(
|
|
1676
|
+
"Client.getExperiment",
|
|
1677
|
+
notIn,
|
|
1678
|
+
() => this.engine.getExperiment(name, defaultParams, decode)
|
|
1679
|
+
);
|
|
1608
1680
|
}
|
|
1609
1681
|
/** Read a killswitch (not user-bound; mirrors {@link Engine.getKillswitch}). */
|
|
1610
1682
|
getKillswitch(name, switchKey) {
|
|
1611
|
-
return this.engine.getKillswitch(name, switchKey);
|
|
1683
|
+
return safeRun("Client.getKillswitch", false, () => this.engine.getKillswitch(name, switchKey));
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Record a conversion/metric event for the bound (identified) user. Delegates
|
|
1687
|
+
* to {@link Engine.track} — so an experiment is end-to-end Client-only (no need
|
|
1688
|
+
* to drop down to the Engine to log a conversion). Fire-and-forget; no-op in
|
|
1689
|
+
* test mode.
|
|
1690
|
+
*/
|
|
1691
|
+
track(eventName, props) {
|
|
1692
|
+
safeRun("Client.track", void 0, () => this.engine.track(eventName, props));
|
|
1693
|
+
}
|
|
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));
|
|
1612
1702
|
}
|
|
1613
1703
|
};
|
|
1614
1704
|
function dispatchSee(problem, consequence, extras, kind) {
|
|
1615
1705
|
if (!_client) {
|
|
1616
|
-
|
|
1706
|
+
logger.warn("[shipeasy] see() called before shipeasy({ clientKey }) \u2014 error dropped");
|
|
1617
1707
|
return;
|
|
1618
1708
|
}
|
|
1619
1709
|
_client.reportError(problem, consequence, extras, kind);
|
|
@@ -1865,6 +1955,7 @@ export {
|
|
|
1865
1955
|
LABEL_MARKER_RE,
|
|
1866
1956
|
LABEL_MARKER_SEP,
|
|
1867
1957
|
LABEL_MARKER_START,
|
|
1958
|
+
LOG_LEVELS,
|
|
1868
1959
|
_resetConfigureForTests,
|
|
1869
1960
|
_resetShipeasyForTests,
|
|
1870
1961
|
attachDevtools,
|