@shipeasy/sdk 6.2.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.
@@ -1,3 +1,7 @@
1
+ type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
2
+ /** All accepted {@link LogLevel} values, in increasing verbosity. */
3
+ declare const LOG_LEVELS: readonly LogLevel[];
4
+
1
5
  type SeeExtras = Record<string, string | number | boolean | null | undefined>;
2
6
  type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
3
7
  /** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
@@ -232,6 +236,14 @@ interface EngineOptions {
232
236
  disableTelemetry?: boolean;
233
237
  /** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
234
238
  telemetryUrl?: string;
239
+ /**
240
+ * How chatty the SDK is on `console` when it catches an error internally.
241
+ * Every public runtime method (getFlag/getConfig/getExperiment/getKillswitch/
242
+ * track/logExposure/see) fails silently — returning a safe default instead of
243
+ * throwing — and surfaces the swallowed error through this level. Ordering:
244
+ * `silent` < `error` < `warn` < `info` < `debug`. Defaults to `"warn"`.
245
+ */
246
+ logLevel?: LogLevel;
235
247
  /**
236
248
  * Suppress automatic exposure logging in `getExperiment` (Statsig's
237
249
  * `disableExposureLogging`). Default false — reading an enrolled variant
@@ -468,6 +480,13 @@ interface ShipeasyClientConfig {
468
480
  autoCollect?: boolean | (Partial<AutoCollectGroups> & {
469
481
  always?: boolean;
470
482
  });
483
+ /**
484
+ * How chatty the SDK is on `console` when it swallows an internal error.
485
+ * `silent` < `error` < `warn` < `info` < `debug`; defaults to `"warn"`. Every
486
+ * runtime read/track/see call fails silently to a safe default and reports the
487
+ * cause at this level. See {@link EngineOptions.logLevel}.
488
+ */
489
+ logLevel?: LogLevel;
471
490
  /**
472
491
  * Disable per-evaluation usage telemetry. Telemetry is ON by default — every
473
492
  * flag/config/experiment/killswitch read fires one fire-and-forget beacon
@@ -760,4 +779,4 @@ interface I18nFacade {
760
779
  }
761
780
  declare const i18n: I18nFacade;
762
781
 
763
- export { type AttributesFn, type AutoCollectGroups, type BootstrapPayload, Client, type ConfigureOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, FLAG_REASONS, type FlagDetail, type FlagReason, type GetConfigOptions, type GetExperimentOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetConfigureForTests, _resetShipeasyForTests, attachDevtools, configure, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, injectCorrelationHeader, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, sameOrigin, see, shipeasy, version };
782
+ export { type AttributesFn, type AutoCollectGroups, type BootstrapPayload, Client, type ConfigureOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, FLAG_REASONS, type FlagDetail, type FlagReason, type GetConfigOptions, type GetExperimentOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, LOG_LEVELS, type LabelAttrs, type LogLevel, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetConfigureForTests, _resetShipeasyForTests, attachDevtools, configure, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, injectCorrelationHeader, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, sameOrigin, see, shipeasy, version };
@@ -1,3 +1,7 @@
1
+ type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
2
+ /** All accepted {@link LogLevel} values, in increasing verbosity. */
3
+ declare const LOG_LEVELS: readonly LogLevel[];
4
+
1
5
  type SeeExtras = Record<string, string | number | boolean | null | undefined>;
2
6
  type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
3
7
  /** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
@@ -232,6 +236,14 @@ interface EngineOptions {
232
236
  disableTelemetry?: boolean;
233
237
  /** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
234
238
  telemetryUrl?: string;
239
+ /**
240
+ * How chatty the SDK is on `console` when it catches an error internally.
241
+ * Every public runtime method (getFlag/getConfig/getExperiment/getKillswitch/
242
+ * track/logExposure/see) fails silently — returning a safe default instead of
243
+ * throwing — and surfaces the swallowed error through this level. Ordering:
244
+ * `silent` < `error` < `warn` < `info` < `debug`. Defaults to `"warn"`.
245
+ */
246
+ logLevel?: LogLevel;
235
247
  /**
236
248
  * Suppress automatic exposure logging in `getExperiment` (Statsig's
237
249
  * `disableExposureLogging`). Default false — reading an enrolled variant
@@ -468,6 +480,13 @@ interface ShipeasyClientConfig {
468
480
  autoCollect?: boolean | (Partial<AutoCollectGroups> & {
469
481
  always?: boolean;
470
482
  });
483
+ /**
484
+ * How chatty the SDK is on `console` when it swallows an internal error.
485
+ * `silent` < `error` < `warn` < `info` < `debug`; defaults to `"warn"`. Every
486
+ * runtime read/track/see call fails silently to a safe default and reports the
487
+ * cause at this level. See {@link EngineOptions.logLevel}.
488
+ */
489
+ logLevel?: LogLevel;
471
490
  /**
472
491
  * Disable per-evaluation usage telemetry. Telemetry is ON by default — every
473
492
  * flag/config/experiment/killswitch read fires one fire-and-forget beacon
@@ -760,4 +779,4 @@ interface I18nFacade {
760
779
  }
761
780
  declare const i18n: I18nFacade;
762
781
 
763
- export { type AttributesFn, type AutoCollectGroups, type BootstrapPayload, Client, type ConfigureOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, FLAG_REASONS, type FlagDetail, type FlagReason, type GetConfigOptions, type GetExperimentOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetConfigureForTests, _resetShipeasyForTests, attachDevtools, configure, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, injectCorrelationHeader, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, sameOrigin, see, shipeasy, version };
782
+ export { type AttributesFn, type AutoCollectGroups, type BootstrapPayload, Client, type ConfigureOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, FLAG_REASONS, type FlagDetail, type FlagReason, type GetConfigOptions, type GetExperimentOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, LOG_LEVELS, type LabelAttrs, type LogLevel, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetConfigureForTests, _resetShipeasyForTests, attachDevtools, configure, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, injectCorrelationHeader, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, sameOrigin, see, shipeasy, version };
@@ -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
- console.warn("[shipeasy] subscriber threw:", String(err));
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
- console.warn(`[shipeasy] getConfig('${name}') decode failed:`, String(err));
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
- console.warn(`[shipeasy] getExperiment('${name}') decode failed:`, String(err));
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
- console.warn("[shipeasy] auto-identify failed:", String(err));
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
- console.warn("[shipeasy] flags.identify called before configureShipeasy()");
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
- const bs = getBootstrap();
1516
- if (bs !== null && name in bs.flags) return bs.flags[name];
1517
- if (!_mountedAndReady) return defaultValue;
1518
- if (_client) return _client.getFlag(name, defaultValue);
1519
- return readGateOverride(name) ?? defaultValue;
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
- 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" };
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
- const bs = getBootstrap();
1530
- if (bs !== null && name in bs.configs) {
1531
- const raw = bs.configs[name];
1532
- if (!decode) return raw;
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(raw);
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
- if (!_client) return fallback;
1557
- return typeof decodeOrOpts === "function" ? _client.getExperiment(name, defaultParams, decodeOrOpts, variants) : _client.getExperiment(name, defaultParams, decodeOrOpts ?? {});
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
- const bs = getBootstrap();
1578
- if (bs !== null && bs.killswitches && name in bs.killswitches) {
1579
- const ks = bs.killswitches[name];
1580
- if (typeof ks === "boolean") return switchKey === void 0 ? ks : false;
1581
- if (switchKey === void 0) return false;
1582
- return ks[switchKey] === true;
1583
- }
1584
- if (!_mountedAndReady) return false;
1585
- return _client?.getKillswitch(name, switchKey) ?? false;
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
- console.warn("[shipeasy] Client identify failed:", String(err));
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,20 +1707,33 @@ 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 this.engine.getFlagDetail(name);
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 this.engine.getConfig(name, decodeOrOpts);
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
- return this.engine.getExperiment(name, defaultParams, decode);
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));
1664
1737
  }
1665
1738
  /**
1666
1739
  * Record a conversion/metric event for the bound (identified) user. Delegates
@@ -1669,7 +1742,7 @@ var Client = class {
1669
1742
  * test mode.
1670
1743
  */
1671
1744
  track(eventName, props) {
1672
- this.engine.track(eventName, props);
1745
+ safeRun("Client.track", void 0, () => this.engine.track(eventName, props));
1673
1746
  }
1674
1747
  /**
1675
1748
  * Log an exposure for `name` at the treatment's render for the bound user.
@@ -1678,12 +1751,12 @@ var Client = class {
1678
1751
  * `disableAutoExposure` to log exposure exactly when you render.
1679
1752
  */
1680
1753
  logExposure(name) {
1681
- this.engine.logExposure(name);
1754
+ safeRun("Client.logExposure", void 0, () => this.engine.logExposure(name));
1682
1755
  }
1683
1756
  };
1684
1757
  function dispatchSee(problem, consequence, extras, kind) {
1685
1758
  if (!_client) {
1686
- console.warn("[shipeasy] see() called before shipeasy({ clientKey }) \u2014 error dropped");
1759
+ logger.warn("[shipeasy] see() called before shipeasy({ clientKey }) \u2014 error dropped");
1687
1760
  return;
1688
1761
  }
1689
1762
  _client.reportError(problem, consequence, extras, kind);
@@ -1936,6 +2009,7 @@ var i18n = {
1936
2009
  LABEL_MARKER_RE,
1937
2010
  LABEL_MARKER_SEP,
1938
2011
  LABEL_MARKER_START,
2012
+ LOG_LEVELS,
1939
2013
  _resetConfigureForTests,
1940
2014
  _resetShipeasyForTests,
1941
2015
  attachDevtools,