@smplkit/sdk 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1549,6 +1549,13 @@ declare class FlagsClient {
1549
1549
  * before using `.get()` on flag handles.
1550
1550
  */
1551
1551
  initialize(): Promise<void>;
1552
+ /**
1553
+ * Synchronous teardown — clears the flag-flush interval and unsubscribes
1554
+ * from WebSocket events. Called by `SmplClient.close()`. Does not flush
1555
+ * pending context observations (use {@link disconnect} for that).
1556
+ * @internal
1557
+ */
1558
+ _close(): void;
1552
1559
  /** Disconnect the flags runtime and release resources. */
1553
1560
  disconnect(): Promise<void>;
1554
1561
  /** Refresh all flag definitions from the server. */
@@ -2568,8 +2575,32 @@ declare class LoggingClient {
2568
2575
  * `install()`.
2569
2576
  *
2570
2577
  * Mirrors Python's `client.logging.install()`. There is no `stop()`.
2578
+ *
2579
+ * Adapter coverage:
2580
+ * - **winston**: pre-existing named loggers (`winston.loggers.*`) and the
2581
+ * default logger are auto-discovered.
2582
+ * - **pino**: pino has no global registry, so only loggers created
2583
+ * through `pino()` / `logger.child()` *after* `install()` runs are
2584
+ * tracked. To bring pre-existing pino loggers under management, recreate
2585
+ * them after install or register them explicitly via
2586
+ * `client.manage.loggers.register([...])`.
2587
+ *
2588
+ * After the initial pass, call {@link refresh} to re-fetch managed levels
2589
+ * from the server and re-apply them onto the native loggers (e.g. after
2590
+ * suspecting drift, or to force a manual sync outside the WebSocket).
2571
2591
  */
2572
2592
  install(): Promise<void>;
2593
+ /**
2594
+ * Re-fetch logger and group levels from the server and re-apply them
2595
+ * onto the registered adapters.
2596
+ *
2597
+ * Diff-based: change listeners only fire for loggers whose level
2598
+ * actually changed (added, removed, or different level), with
2599
+ * `source: "manual"`. Mirrors Python's `client.logging.refresh()`.
2600
+ *
2601
+ * @throws SmplError if `install()` has not been called.
2602
+ */
2603
+ refresh(): Promise<void>;
2573
2604
  /**
2574
2605
  * @deprecated Use {@link LoggingClient.install}. Retained as a backwards-
2575
2606
  * compatible alias.
@@ -2599,6 +2630,14 @@ declare class LoggingClient {
2599
2630
  private _handleGroupChanged;
2600
2631
  private _handleGroupDeleted;
2601
2632
  private _handleLoggersChanged;
2633
+ /**
2634
+ * Full refetch of loggers + log_groups, apply resolved levels to
2635
+ * adapters, diff against local stores and fire change listeners
2636
+ * (global once, per-key for each changed id). Shared between the
2637
+ * `loggers_changed` WS handler and the public `refresh()` method.
2638
+ * @internal
2639
+ */
2640
+ private _resolveAndFire;
2602
2641
  /** Fetch a single logger by key. Returns null if not found. @internal */
2603
2642
  private _fetchSingleLogger;
2604
2643
  /** Fetch a single log group by key. Returns null if not found. @internal */
package/dist/index.d.ts CHANGED
@@ -1549,6 +1549,13 @@ declare class FlagsClient {
1549
1549
  * before using `.get()` on flag handles.
1550
1550
  */
1551
1551
  initialize(): Promise<void>;
1552
+ /**
1553
+ * Synchronous teardown — clears the flag-flush interval and unsubscribes
1554
+ * from WebSocket events. Called by `SmplClient.close()`. Does not flush
1555
+ * pending context observations (use {@link disconnect} for that).
1556
+ * @internal
1557
+ */
1558
+ _close(): void;
1552
1559
  /** Disconnect the flags runtime and release resources. */
1553
1560
  disconnect(): Promise<void>;
1554
1561
  /** Refresh all flag definitions from the server. */
@@ -2568,8 +2575,32 @@ declare class LoggingClient {
2568
2575
  * `install()`.
2569
2576
  *
2570
2577
  * Mirrors Python's `client.logging.install()`. There is no `stop()`.
2578
+ *
2579
+ * Adapter coverage:
2580
+ * - **winston**: pre-existing named loggers (`winston.loggers.*`) and the
2581
+ * default logger are auto-discovered.
2582
+ * - **pino**: pino has no global registry, so only loggers created
2583
+ * through `pino()` / `logger.child()` *after* `install()` runs are
2584
+ * tracked. To bring pre-existing pino loggers under management, recreate
2585
+ * them after install or register them explicitly via
2586
+ * `client.manage.loggers.register([...])`.
2587
+ *
2588
+ * After the initial pass, call {@link refresh} to re-fetch managed levels
2589
+ * from the server and re-apply them onto the native loggers (e.g. after
2590
+ * suspecting drift, or to force a manual sync outside the WebSocket).
2571
2591
  */
2572
2592
  install(): Promise<void>;
2593
+ /**
2594
+ * Re-fetch logger and group levels from the server and re-apply them
2595
+ * onto the registered adapters.
2596
+ *
2597
+ * Diff-based: change listeners only fire for loggers whose level
2598
+ * actually changed (added, removed, or different level), with
2599
+ * `source: "manual"`. Mirrors Python's `client.logging.refresh()`.
2600
+ *
2601
+ * @throws SmplError if `install()` has not been called.
2602
+ */
2603
+ refresh(): Promise<void>;
2573
2604
  /**
2574
2605
  * @deprecated Use {@link LoggingClient.install}. Retained as a backwards-
2575
2606
  * compatible alias.
@@ -2599,6 +2630,14 @@ declare class LoggingClient {
2599
2630
  private _handleGroupChanged;
2600
2631
  private _handleGroupDeleted;
2601
2632
  private _handleLoggersChanged;
2633
+ /**
2634
+ * Full refetch of loggers + log_groups, apply resolved levels to
2635
+ * adapters, diff against local stores and fire change listeners
2636
+ * (global once, per-key for each changed id). Shared between the
2637
+ * `loggers_changed` WS handler and the public `refresh()` method.
2638
+ * @internal
2639
+ */
2640
+ private _resolveAndFire;
2602
2641
  /** Fetch a single logger by key. Returns null if not found. @internal */
2603
2642
  private _fetchSingleLogger;
2604
2643
  /** Fetch a single log group by key. Returns null if not found. @internal */
package/dist/index.js CHANGED
@@ -20490,6 +20490,30 @@ var FlagsClient = class {
20490
20490
  this._flagFlushTimer = setInterval(() => {
20491
20491
  void this._flushFlags();
20492
20492
  }, FLAG_REGISTRATION_FLUSH_INTERVAL_MS);
20493
+ if (typeof this._flagFlushTimer === "object" && "unref" in this._flagFlushTimer) {
20494
+ this._flagFlushTimer.unref();
20495
+ }
20496
+ }
20497
+ /**
20498
+ * Synchronous teardown — clears the flag-flush interval and unsubscribes
20499
+ * from WebSocket events. Called by `SmplClient.close()`. Does not flush
20500
+ * pending context observations (use {@link disconnect} for that).
20501
+ * @internal
20502
+ */
20503
+ _close() {
20504
+ if (this._flagFlushTimer !== null) {
20505
+ clearInterval(this._flagFlushTimer);
20506
+ this._flagFlushTimer = null;
20507
+ }
20508
+ if (this._wsManager !== null) {
20509
+ this._wsManager.off("flag_changed", this._handleFlagChanged);
20510
+ this._wsManager.off("flag_deleted", this._handleFlagDeleted);
20511
+ this._wsManager.off("flags_changed", this._handleFlagsChanged);
20512
+ this._wsManager = null;
20513
+ }
20514
+ this._cache.clear();
20515
+ this._initialized = false;
20516
+ this._environment = null;
20493
20517
  }
20494
20518
  /** Disconnect the flags runtime and release resources. */
20495
20519
  async disconnect() {
@@ -20646,6 +20670,9 @@ var FlagsClient = class {
20646
20670
  this._flagFlushTimer = setInterval(() => {
20647
20671
  void this._flushFlags();
20648
20672
  }, FLAG_REGISTRATION_FLUSH_INTERVAL_MS);
20673
+ if (typeof this._flagFlushTimer === "object" && "unref" in this._flagFlushTimer) {
20674
+ this._flagFlushTimer.unref();
20675
+ }
20649
20676
  }
20650
20677
  // ------------------------------------------------------------------
20651
20678
  // Internal: event handlers (called by SharedWebSocket)
@@ -20993,10 +21020,39 @@ var LoggingClient = class {
20993
21020
  * `install()`.
20994
21021
  *
20995
21022
  * Mirrors Python's `client.logging.install()`. There is no `stop()`.
21023
+ *
21024
+ * Adapter coverage:
21025
+ * - **winston**: pre-existing named loggers (`winston.loggers.*`) and the
21026
+ * default logger are auto-discovered.
21027
+ * - **pino**: pino has no global registry, so only loggers created
21028
+ * through `pino()` / `logger.child()` *after* `install()` runs are
21029
+ * tracked. To bring pre-existing pino loggers under management, recreate
21030
+ * them after install or register them explicitly via
21031
+ * `client.manage.loggers.register([...])`.
21032
+ *
21033
+ * After the initial pass, call {@link refresh} to re-fetch managed levels
21034
+ * from the server and re-apply them onto the native loggers (e.g. after
21035
+ * suspecting drift, or to force a manual sync outside the WebSocket).
20996
21036
  */
20997
21037
  async install() {
20998
21038
  return this._installInternal();
20999
21039
  }
21040
+ /**
21041
+ * Re-fetch logger and group levels from the server and re-apply them
21042
+ * onto the registered adapters.
21043
+ *
21044
+ * Diff-based: change listeners only fire for loggers whose level
21045
+ * actually changed (added, removed, or different level), with
21046
+ * `source: "manual"`. Mirrors Python's `client.logging.refresh()`.
21047
+ *
21048
+ * @throws SmplError if `install()` has not been called.
21049
+ */
21050
+ async refresh() {
21051
+ if (!this._started) {
21052
+ throw new SmplError("Logging not installed. Call install() first.");
21053
+ }
21054
+ await this._resolveAndFire("manual");
21055
+ }
21000
21056
  /**
21001
21057
  * @deprecated Use {@link LoggingClient.install}. Retained as a backwards-
21002
21058
  * compatible alias.
@@ -21069,6 +21125,9 @@ var LoggingClient = class {
21069
21125
  this._loggerFlushTimer = setInterval(() => {
21070
21126
  void this._flushLoggerBuffer();
21071
21127
  }, 3e4);
21128
+ if (typeof this._loggerFlushTimer === "object" && "unref" in this._loggerFlushTimer) {
21129
+ this._loggerFlushTimer.unref();
21130
+ }
21072
21131
  this._started = true;
21073
21132
  }
21074
21133
  // ------------------------------------------------------------------
@@ -21340,63 +21399,75 @@ var LoggingClient = class {
21340
21399
  };
21341
21400
  _handleLoggersChanged = (_data) => {
21342
21401
  debug("websocket", `loggers_changed event received`);
21343
- void Promise.all([this._listLoggers(), this._listLogGroups()]).then(([serverLoggers, serverGroups]) => {
21344
- debug("resolution", `resolution pass (trigger: loggers_changed event)`);
21345
- const changedLoggerIds = /* @__PURE__ */ new Set();
21346
- const newLoggerKeys = new Set(serverLoggers.map((l) => l.id));
21347
- for (const logger of serverLoggers) {
21348
- const key = logger.id;
21349
- const oldLevel = this._loggerStore[key] ?? null;
21350
- const newLevel = logger.level ?? null;
21351
- if (oldLevel !== newLevel || !(key in this._loggerStore)) {
21352
- changedLoggerIds.add(key);
21353
- this._loggerStore[key] = newLevel;
21354
- }
21355
- }
21356
- for (const key of Object.keys(this._loggerStore)) {
21357
- if (!newLoggerKeys.has(key)) {
21358
- changedLoggerIds.add(key);
21359
- delete this._loggerStore[key];
21360
- }
21361
- }
21362
- for (const group of serverGroups) {
21363
- this._groupStore[group.id] = group.level ?? null;
21364
- }
21365
- this._applyLevels(serverLoggers);
21366
- if (changedLoggerIds.size === 0) return;
21367
- const [firstKey] = changedLoggerIds;
21368
- const firstLogger = serverLoggers.find((l) => l.id === firstKey);
21369
- const globalEvent = new LoggerChangeEvent({
21370
- id: firstKey,
21371
- level: firstLogger?.level ?? null,
21372
- source: "websocket"
21373
- });
21374
- for (const cb of this._globalListeners) {
21375
- try {
21376
- cb(globalEvent);
21377
- } catch {
21378
- }
21402
+ void this._resolveAndFire("websocket").catch(() => {
21403
+ });
21404
+ };
21405
+ /**
21406
+ * Full refetch of loggers + log_groups, apply resolved levels to
21407
+ * adapters, diff against local stores and fire change listeners
21408
+ * (global once, per-key for each changed id). Shared between the
21409
+ * `loggers_changed` WS handler and the public `refresh()` method.
21410
+ * @internal
21411
+ */
21412
+ async _resolveAndFire(source) {
21413
+ const [serverLoggers, serverGroups] = await Promise.all([
21414
+ this._listLoggers(),
21415
+ this._listLogGroups()
21416
+ ]);
21417
+ debug("resolution", `resolution pass (trigger: ${source})`);
21418
+ const changedLoggerIds = /* @__PURE__ */ new Set();
21419
+ const newLoggerKeys = new Set(serverLoggers.map((l) => l.id));
21420
+ for (const logger of serverLoggers) {
21421
+ const key = logger.id;
21422
+ const oldLevel = this._loggerStore[key] ?? null;
21423
+ const newLevel = logger.level ?? null;
21424
+ if (oldLevel !== newLevel || !(key in this._loggerStore)) {
21425
+ changedLoggerIds.add(key);
21426
+ this._loggerStore[key] = newLevel;
21427
+ }
21428
+ }
21429
+ for (const key of Object.keys(this._loggerStore)) {
21430
+ if (!newLoggerKeys.has(key)) {
21431
+ changedLoggerIds.add(key);
21432
+ delete this._loggerStore[key];
21433
+ }
21434
+ }
21435
+ for (const group of serverGroups) {
21436
+ this._groupStore[group.id] = group.level ?? null;
21437
+ }
21438
+ this._applyLevels(serverLoggers);
21439
+ if (changedLoggerIds.size === 0) return;
21440
+ const [firstKey] = changedLoggerIds;
21441
+ const firstLogger = serverLoggers.find((l) => l.id === firstKey);
21442
+ const globalEvent = new LoggerChangeEvent({
21443
+ id: firstKey,
21444
+ level: firstLogger?.level ?? null,
21445
+ source
21446
+ });
21447
+ for (const cb of this._globalListeners) {
21448
+ try {
21449
+ cb(globalEvent);
21450
+ } catch {
21379
21451
  }
21380
- for (const key of changedLoggerIds) {
21381
- const keyCallbacks = this._keyListeners.get(key);
21382
- if (keyCallbacks) {
21383
- const l = serverLoggers.find((x) => x.id === key);
21384
- const keyEvent = new LoggerChangeEvent({
21385
- id: key,
21386
- level: l?.level ?? null,
21387
- source: "websocket"
21388
- });
21389
- for (const cb of keyCallbacks) {
21390
- try {
21391
- cb(keyEvent);
21392
- } catch {
21393
- }
21452
+ }
21453
+ for (const key of changedLoggerIds) {
21454
+ const keyCallbacks = this._keyListeners.get(key);
21455
+ if (keyCallbacks) {
21456
+ const l = serverLoggers.find((x) => x.id === key);
21457
+ const keyEvent = new LoggerChangeEvent({
21458
+ id: key,
21459
+ level: l?.level ?? null,
21460
+ source
21461
+ });
21462
+ for (const cb of keyCallbacks) {
21463
+ try {
21464
+ cb(keyEvent);
21465
+ } catch {
21394
21466
  }
21395
21467
  }
21396
21468
  }
21397
- }).catch(() => {
21398
- });
21399
- };
21469
+ }
21470
+ }
21400
21471
  // ------------------------------------------------------------------
21401
21472
  // Internal: single-resource fetchers
21402
21473
  // ------------------------------------------------------------------
@@ -21939,6 +22010,7 @@ var SmplClient = class {
21939
22010
  if (this._metrics !== null) {
21940
22011
  this._metrics.close();
21941
22012
  }
22013
+ this.flags._close();
21942
22014
  this.logging._close();
21943
22015
  if (this._wsManager !== null) {
21944
22016
  this._wsManager.stop();