@smplkit/sdk 3.0.49 → 3.0.50

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.cjs CHANGED
@@ -17597,6 +17597,9 @@ function _parseDebugEnv(value) {
17597
17597
  return v === "1" || v === "true" || v === "yes";
17598
17598
  }
17599
17599
  var _DEBUG_ENABLED = _parseDebugEnv(process.env.SMPLKIT_DEBUG ?? "");
17600
+ function isDebugEnabled() {
17601
+ return _DEBUG_ENABLED;
17602
+ }
17600
17603
  function enableDebug() {
17601
17604
  _DEBUG_ENABLED = true;
17602
17605
  }
@@ -21774,6 +21777,72 @@ var FlagsClient = class {
21774
21777
 
21775
21778
  // src/logging/client.ts
21776
21779
  var import_openapi_fetch5 = __toESM(require("openapi-fetch"), 1);
21780
+
21781
+ // src/logging/_resolution.ts
21782
+ var FALLBACK_LEVEL = "INFO";
21783
+ function resolveLevel(loggerId, environment, loggers, groups) {
21784
+ const direct = _resolveForEntry(loggerId, environment, loggers, groups);
21785
+ if (direct !== null) {
21786
+ if (isDebugEnabled()) {
21787
+ const source = _findResolutionSource(loggerId, environment, loggers, groups);
21788
+ debug("resolution", `${loggerId} -> ${direct} (source: ${source})`);
21789
+ }
21790
+ return direct;
21791
+ }
21792
+ const parts = loggerId.split(".");
21793
+ for (let i = parts.length - 1; i > 0; i--) {
21794
+ const ancestorId = parts.slice(0, i).join(".");
21795
+ const result = _resolveForEntry(ancestorId, environment, loggers, groups);
21796
+ if (result !== null) {
21797
+ debug("resolution", `${loggerId} -> ${result} (source: ancestor "${ancestorId}")`);
21798
+ return result;
21799
+ }
21800
+ }
21801
+ debug("resolution", `${loggerId} -> ${FALLBACK_LEVEL} (source: system default)`);
21802
+ return FALLBACK_LEVEL;
21803
+ }
21804
+ function _resolveForEntry(loggerId, environment, loggers, groups) {
21805
+ const entry = loggers[loggerId];
21806
+ if (entry === void 0) return null;
21807
+ const envLevel = _envLevel(entry.environments, environment);
21808
+ if (envLevel !== null) return envLevel;
21809
+ if (entry.level !== null && entry.level !== void 0) return entry.level;
21810
+ return _resolveGroupChain(entry.group, environment, groups);
21811
+ }
21812
+ function _findResolutionSource(loggerId, environment, loggers, groups) {
21813
+ const entry = loggers[loggerId];
21814
+ if (entry === void 0) return "not found";
21815
+ const envLevel = _envLevel(entry.environments, environment);
21816
+ if (envLevel !== null) return `env override "${environment}"`;
21817
+ if (entry.level !== null && entry.level !== void 0) return "base level";
21818
+ const groupResult = _resolveGroupChain(entry.group, environment, groups);
21819
+ if (groupResult !== null) return `group "${entry.group}"`;
21820
+ return "unknown";
21821
+ }
21822
+ function _resolveGroupChain(groupId, environment, groups) {
21823
+ const visited = /* @__PURE__ */ new Set();
21824
+ let currentId = groupId;
21825
+ while (currentId !== null && currentId !== void 0 && !visited.has(currentId)) {
21826
+ visited.add(currentId);
21827
+ const group = groups[currentId];
21828
+ if (group === void 0) break;
21829
+ const envLevel = _envLevel(group.environments, environment);
21830
+ if (envLevel !== null) return envLevel;
21831
+ if (group.level !== null && group.level !== void 0) return group.level;
21832
+ currentId = group.group;
21833
+ }
21834
+ return null;
21835
+ }
21836
+ function _envLevel(envs, environment) {
21837
+ if (!envs || typeof envs !== "object") return null;
21838
+ const envData = envs[environment];
21839
+ if (!envData || typeof envData !== "object") return null;
21840
+ const level = envData.level;
21841
+ if (level === null || level === void 0) return null;
21842
+ return level;
21843
+ }
21844
+
21845
+ // src/logging/client.ts
21777
21846
  var LOGGING_BASE_URL = "https://logging.smplkit.com";
21778
21847
  var LoggerRegistrationBuffer2 = class {
21779
21848
  _seen = /* @__PURE__ */ new Set();
@@ -21815,11 +21884,20 @@ var LoggingClient = class {
21815
21884
  _explicitAdapters = false;
21816
21885
  _loggerBuffer = new LoggerRegistrationBuffer2();
21817
21886
  _loggerFlushTimer = null;
21818
- // Local stores for diff-based listener firing
21819
- _loggerStore = {};
21820
- // key -> level
21821
- _groupStore = {};
21822
- // key -> level
21887
+ // Caches consulted by the resolution algorithm. The runtime client mutates
21888
+ // these from install(), refresh(), and the WebSocket handlers; resolveLevel
21889
+ // reads them on every apply.
21890
+ _loggersCache = {};
21891
+ _groupsCache = {};
21892
+ // Resolved-level snapshot, used to decide whether to fire change listeners.
21893
+ // Keyed by logger id; storing the *resolved* level (not raw `logger.level`)
21894
+ // means a group-driven level shift fires listeners even when the logger's
21895
+ // own level is untouched.
21896
+ _resolvedLevelStore = {};
21897
+ // Adapter-known logger names. The adapter knows loggers by their original
21898
+ // name (e.g. `com.acme.payments`); the cache is keyed by the same string
21899
+ // since the TypeScript SDK does not normalize.
21900
+ _knownLoggerNames = /* @__PURE__ */ new Set();
21823
21901
  /** @internal */
21824
21902
  constructor(apiKey, ensureWs, timeout, baseUrl, extraHeaders) {
21825
21903
  this._apiKey = apiKey;
@@ -22012,6 +22090,7 @@ var LoggingClient = class {
22012
22090
  const loggers = adapter.discover();
22013
22091
  for (const { name, level } of loggers) {
22014
22092
  this._loggerBuffer.add(name, level, level, service, environment);
22093
+ this._knownLoggerNames.add(name);
22015
22094
  discoveredCount++;
22016
22095
  }
22017
22096
  } catch {
@@ -22044,13 +22123,9 @@ var LoggingClient = class {
22044
22123
  "api",
22045
22124
  `fetched ${serverLoggers.length} logger(s) and ${serverGroups.length} group(s) from server`
22046
22125
  );
22047
- this._applyLevels(serverLoggers);
22048
- for (const l of serverLoggers) {
22049
- this._loggerStore[l.id] = l.level;
22050
- }
22051
- for (const g of serverGroups) {
22052
- this._groupStore[g.id] = g.level;
22053
- }
22126
+ this._loggersCache = this._buildLoggersCache(serverLoggers);
22127
+ this._groupsCache = this._buildGroupsCache(serverGroups);
22128
+ this._applyLevels();
22054
22129
  } catch {
22055
22130
  }
22056
22131
  this._wsManager = this._ensureWs();
@@ -22142,41 +22217,107 @@ var LoggingClient = class {
22142
22217
  }
22143
22218
  return adapters;
22144
22219
  }
22145
- /** Apply resolved levels from server loggers to all adapters. */
22146
- _applyLevels(serverLoggers) {
22147
- for (const logger of serverLoggers) {
22148
- if (!logger.level) continue;
22149
- const env = this._parent?._environment;
22150
- let effectiveLevel = logger.level;
22151
- if (env && logger.environments) {
22152
- const envOverride = logger.environments[env];
22153
- if (envOverride?.level) {
22154
- effectiveLevel = envOverride.level;
22155
- }
22220
+ /**
22221
+ * Refresh resolved levels and apply them to adapter-known loggers.
22222
+ *
22223
+ * Computes a resolved level (via {@link resolveLevel}) for every entry
22224
+ * currently in `_loggersCache` *and* every adapter-known logger name —
22225
+ * even loggers whose own `level` is `null`, because they may inherit
22226
+ * via group chain or dot-notation ancestry.
22227
+ *
22228
+ * The full resolved-level snapshot is stored in `_resolvedLevelStore`
22229
+ * so callers can diff pre-vs-post and fire change listeners on actual
22230
+ * effective-level deltas (group-driven changes included). Adapter pushes
22231
+ * only happen for adapter-known names where `managed !== false`.
22232
+ * @internal
22233
+ */
22234
+ _applyLevels() {
22235
+ const environment = this._parent?._environment ?? "";
22236
+ const newResolved = {};
22237
+ for (const id of Object.keys(this._loggersCache)) {
22238
+ newResolved[id] = resolveLevel(id, environment, this._loggersCache, this._groupsCache);
22239
+ }
22240
+ for (const name of this._knownLoggerNames) {
22241
+ if (!(name in newResolved)) {
22242
+ newResolved[name] = resolveLevel(name, environment, this._loggersCache, this._groupsCache);
22156
22243
  }
22157
- debug("resolution", `${logger.id} -> ${effectiveLevel}`);
22244
+ }
22245
+ this._resolvedLevelStore = newResolved;
22246
+ for (const name of this._knownLoggerNames) {
22247
+ const resolved = newResolved[name];
22158
22248
  const metrics = this._parent?._metrics;
22159
22249
  if (metrics) {
22160
- metrics.record("logging.level_changes", 1, "changes", { logger: logger.id });
22250
+ metrics.record("logging.level_changes", 1, "changes", { logger: name });
22161
22251
  }
22162
22252
  for (const adapter of this._adapters) {
22163
22253
  try {
22164
- debug("adapter", `setLevel(${logger.id}, ${effectiveLevel})`);
22165
- adapter.applyLevel(logger.id, effectiveLevel);
22254
+ debug("adapter", `setLevel(${name}, ${resolved})`);
22255
+ adapter.applyLevel(name, resolved);
22166
22256
  } catch {
22167
22257
  }
22168
22258
  }
22169
22259
  }
22170
22260
  }
22261
+ /** @internal Convert a server Logger list into the resolution cache. */
22262
+ _buildLoggersCache(loggers) {
22263
+ const out = {};
22264
+ for (const l of loggers) {
22265
+ if (l.id === null) continue;
22266
+ out[l.id] = this._loggerToCacheEntry(l);
22267
+ }
22268
+ return out;
22269
+ }
22270
+ /** @internal Convert a server LogGroup list into the resolution cache. */
22271
+ _buildGroupsCache(groups) {
22272
+ const out = {};
22273
+ for (const g of groups) {
22274
+ if (g.id === null) continue;
22275
+ out[g.id] = this._groupToCacheEntry(g);
22276
+ }
22277
+ return out;
22278
+ }
22279
+ /** @internal */
22280
+ _loggerToCacheEntry(l) {
22281
+ return {
22282
+ level: l.level ?? null,
22283
+ group: l.group ?? null,
22284
+ managed: l.managed ?? null,
22285
+ environments: l.environments ?? null
22286
+ };
22287
+ }
22288
+ /** @internal */
22289
+ _groupToCacheEntry(g) {
22290
+ return {
22291
+ level: g.level ?? null,
22292
+ group: g.group ?? null,
22293
+ environments: g.environments ?? null
22294
+ };
22295
+ }
22171
22296
  /** Called by adapter hooks when a new logger is created in the framework. */
22172
22297
  _onAdapterNewLogger(name, level) {
22173
22298
  debug("discovery", `new logger intercepted at runtime: ${name}`);
22174
22299
  const service = this._parent?._service ?? null;
22175
22300
  const environment = this._parent?._environment ?? null;
22176
22301
  this._loggerBuffer.add(name, level, level, service, environment);
22302
+ this._knownLoggerNames.add(name);
22177
22303
  if (this._loggerBuffer.pendingCount >= 50) {
22178
22304
  void this._flushLoggerBuffer();
22179
22305
  }
22306
+ if (this._started) {
22307
+ const resolved = resolveLevel(
22308
+ name,
22309
+ this._parent?._environment ?? "",
22310
+ this._loggersCache,
22311
+ this._groupsCache
22312
+ );
22313
+ this._resolvedLevelStore[name] = resolved;
22314
+ for (const adapter of this._adapters) {
22315
+ try {
22316
+ adapter.applyLevel(name, resolved);
22317
+ } catch {
22318
+ }
22319
+ }
22320
+ }
22180
22321
  }
22181
22322
  /** Flush buffered loggers to the bulk-register endpoint. */
22182
22323
  async _flushLoggerBuffer() {
@@ -22210,33 +22351,14 @@ var LoggingClient = class {
22210
22351
  const id = data.id;
22211
22352
  if (!id) return;
22212
22353
  void this._fetchSingleLogger(id).then((logger) => {
22213
- const oldLevel = this._loggerStore[id] ?? null;
22214
- const newLevel = logger?.level ?? null;
22215
- if (oldLevel === newLevel) return;
22216
- this._loggerStore[id] = newLevel;
22217
- if (logger) {
22218
- this._applyLevels([logger]);
22219
- }
22220
- const event = new LoggerChangeEvent({
22221
- id,
22222
- level: newLevel,
22223
- source: "websocket"
22224
- });
22225
- for (const cb of this._globalListeners) {
22226
- try {
22227
- cb(event);
22228
- } catch {
22229
- }
22230
- }
22231
- const idCallbacks = this._keyListeners.get(id);
22232
- if (idCallbacks) {
22233
- for (const cb of idCallbacks) {
22234
- try {
22235
- cb(event);
22236
- } catch {
22237
- }
22238
- }
22354
+ const preResolved = { ...this._resolvedLevelStore };
22355
+ if (logger !== null) {
22356
+ this._loggersCache[id] = this._loggerToCacheEntry(logger);
22357
+ } else {
22358
+ delete this._loggersCache[id];
22239
22359
  }
22360
+ this._applyLevels();
22361
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket");
22240
22362
  }).catch((err) => {
22241
22363
  debug(
22242
22364
  "websocket",
@@ -22248,50 +22370,25 @@ var LoggingClient = class {
22248
22370
  debug("websocket", `logger_deleted event received: ${JSON.stringify(data)}`);
22249
22371
  const id = data.id;
22250
22372
  if (!id) return;
22251
- delete this._loggerStore[id];
22252
- const event = new LoggerChangeEvent({
22253
- id,
22254
- level: null,
22255
- source: "websocket",
22256
- deleted: true
22257
- });
22258
- for (const cb of this._globalListeners) {
22259
- try {
22260
- cb(event);
22261
- } catch (err) {
22262
- debug(
22263
- "websocket",
22264
- `logger_deleted listener error: ${err instanceof Error ? err.message : String(err)}`
22265
- );
22266
- }
22267
- }
22268
- const idCallbacks = this._keyListeners.get(id);
22269
- if (idCallbacks) {
22270
- for (const cb of idCallbacks) {
22271
- try {
22272
- cb(event);
22273
- } catch (err) {
22274
- debug(
22275
- "websocket",
22276
- `logger_deleted key listener error: ${err instanceof Error ? err.message : String(err)}`
22277
- );
22278
- }
22279
- }
22280
- }
22373
+ delete this._loggersCache[id];
22374
+ const preResolved = { ...this._resolvedLevelStore };
22375
+ this._applyLevels();
22376
+ this._emitDeletedEvent(id, "websocket");
22377
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket", /* @__PURE__ */ new Set([id]));
22281
22378
  };
22282
22379
  _handleGroupChanged = (data) => {
22283
22380
  debug("websocket", `group_changed event received: ${JSON.stringify(data)}`);
22284
22381
  const id = data.id;
22285
22382
  if (!id) return;
22286
22383
  void this._fetchSingleGroup(id).then((group) => {
22287
- const oldLevel = this._groupStore[id] ?? null;
22288
- const newLevel = group?.level ?? null;
22289
- if (oldLevel === newLevel) return;
22290
- this._groupStore[id] = newLevel;
22291
- void this._listLoggers().then((loggers) => {
22292
- this._applyLevels(loggers);
22293
- }).catch(() => {
22294
- });
22384
+ const preResolved = { ...this._resolvedLevelStore };
22385
+ if (group !== null) {
22386
+ this._groupsCache[id] = this._groupToCacheEntry(group);
22387
+ } else {
22388
+ delete this._groupsCache[id];
22389
+ }
22390
+ this._applyLevels();
22391
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket");
22295
22392
  }).catch((err) => {
22296
22393
  debug(
22297
22394
  "websocket",
@@ -22303,11 +22400,68 @@ var LoggingClient = class {
22303
22400
  debug("websocket", `group_deleted event received: ${JSON.stringify(data)}`);
22304
22401
  const id = data.id;
22305
22402
  if (!id) return;
22306
- delete this._groupStore[id];
22403
+ delete this._groupsCache[id];
22404
+ const preResolved = { ...this._resolvedLevelStore };
22405
+ this._applyLevels();
22406
+ this._emitDeletedEvent(id, "websocket");
22407
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket", /* @__PURE__ */ new Set([id]));
22408
+ };
22409
+ /**
22410
+ * @internal Fire change listeners for every logger whose resolved level
22411
+ * changed between `pre` and `post`. Stores resolved levels, not raw
22412
+ * `logger.level`, so a group-driven change actually fires. Keys that
22413
+ * disappeared from `post` are emitted with `deleted: true`.
22414
+ */
22415
+ _fireDeltas(pre, post, source, excludeIds) {
22416
+ const changed = [];
22417
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(pre), ...Object.keys(post)]);
22418
+ for (const k of allKeys) {
22419
+ if (excludeIds?.has(k)) continue;
22420
+ if (pre[k] !== post[k]) changed.push(k);
22421
+ }
22422
+ if (changed.length === 0) return;
22423
+ const buildEvent = (id) => {
22424
+ const isDeletion = id in pre && !(id in post);
22425
+ const fields = {
22426
+ id,
22427
+ source,
22428
+ level: post[id] ?? null
22429
+ };
22430
+ if (isDeletion) fields.deleted = true;
22431
+ return new LoggerChangeEvent(fields);
22432
+ };
22433
+ const firstKey = changed[0];
22434
+ const globalEvent = buildEvent(firstKey);
22435
+ for (const cb of this._globalListeners) {
22436
+ try {
22437
+ cb(globalEvent);
22438
+ } catch {
22439
+ }
22440
+ }
22441
+ for (const k of changed) {
22442
+ const keyCallbacks = this._keyListeners.get(k);
22443
+ if (keyCallbacks) {
22444
+ const event = buildEvent(k);
22445
+ for (const cb of keyCallbacks) {
22446
+ try {
22447
+ cb(event);
22448
+ } catch {
22449
+ }
22450
+ }
22451
+ }
22452
+ }
22453
+ }
22454
+ /**
22455
+ * @internal Always emit a deleted event for `id` on global and per-key
22456
+ * listeners. Used by `logger_deleted` / `group_deleted` handlers to
22457
+ * preserve the explicit notification contract even when nothing in the
22458
+ * resolved-level store changed (e.g. server removed an unknown logger).
22459
+ */
22460
+ _emitDeletedEvent(id, source) {
22307
22461
  const event = new LoggerChangeEvent({
22308
22462
  id,
22309
22463
  level: null,
22310
- source: "websocket",
22464
+ source,
22311
22465
  deleted: true
22312
22466
  });
22313
22467
  for (const cb of this._globalListeners) {
@@ -22316,7 +22470,7 @@ var LoggingClient = class {
22316
22470
  } catch (err) {
22317
22471
  debug(
22318
22472
  "websocket",
22319
- `group_deleted listener error: ${err instanceof Error ? err.message : String(err)}`
22473
+ `deleted listener error: ${err instanceof Error ? err.message : String(err)}`
22320
22474
  );
22321
22475
  }
22322
22476
  }
@@ -22328,22 +22482,22 @@ var LoggingClient = class {
22328
22482
  } catch (err) {
22329
22483
  debug(
22330
22484
  "websocket",
22331
- `group_deleted key listener error: ${err instanceof Error ? err.message : String(err)}`
22485
+ `deleted key listener error: ${err instanceof Error ? err.message : String(err)}`
22332
22486
  );
22333
22487
  }
22334
22488
  }
22335
22489
  }
22336
- };
22490
+ }
22337
22491
  _handleLoggersChanged = (_data) => {
22338
22492
  debug("websocket", `loggers_changed event received`);
22339
22493
  void this._resolveAndFire("websocket").catch(() => {
22340
22494
  });
22341
22495
  };
22342
22496
  /**
22343
- * Full refetch of loggers + log_groups, apply resolved levels to
22344
- * adapters, diff against local stores and fire change listeners
22345
- * (global once, per-key for each changed id). Shared between the
22346
- * `loggers_changed` WS handler and the public `refresh()` method.
22497
+ * Full refetch of loggers + log_groups, rebuild the caches, re-resolve
22498
+ * every adapter-known logger, and fire change listeners on resolved-level
22499
+ * deltas. Shared between the `loggers_changed` WS handler and the
22500
+ * public `refresh()` method.
22347
22501
  * @internal
22348
22502
  */
22349
22503
  async _resolveAndFire(source) {
@@ -22352,58 +22506,12 @@ var LoggingClient = class {
22352
22506
  this._listLogGroups()
22353
22507
  ]);
22354
22508
  debug("resolution", `resolution pass (trigger: ${source})`);
22355
- const changedLoggerIds = /* @__PURE__ */ new Set();
22356
- const newLoggerKeys = new Set(serverLoggers.map((l) => l.id));
22357
- for (const logger of serverLoggers) {
22358
- const key = logger.id;
22359
- const oldLevel = this._loggerStore[key] ?? null;
22360
- const newLevel = logger.level ?? null;
22361
- if (oldLevel !== newLevel || !(key in this._loggerStore)) {
22362
- changedLoggerIds.add(key);
22363
- this._loggerStore[key] = newLevel;
22364
- }
22365
- }
22366
- for (const key of Object.keys(this._loggerStore)) {
22367
- if (!newLoggerKeys.has(key)) {
22368
- changedLoggerIds.add(key);
22369
- delete this._loggerStore[key];
22370
- }
22371
- }
22372
- for (const group of serverGroups) {
22373
- this._groupStore[group.id] = group.level ?? null;
22374
- }
22375
- this._applyLevels(serverLoggers);
22376
- if (changedLoggerIds.size === 0) return;
22377
- const [firstKey] = changedLoggerIds;
22378
- const firstLogger = serverLoggers.find((l) => l.id === firstKey);
22379
- const globalEvent = new LoggerChangeEvent({
22380
- id: firstKey,
22381
- level: firstLogger?.level ?? null,
22382
- source
22383
- });
22384
- for (const cb of this._globalListeners) {
22385
- try {
22386
- cb(globalEvent);
22387
- } catch {
22388
- }
22389
- }
22390
- for (const key of changedLoggerIds) {
22391
- const keyCallbacks = this._keyListeners.get(key);
22392
- if (keyCallbacks) {
22393
- const l = serverLoggers.find((x) => x.id === key);
22394
- const keyEvent = new LoggerChangeEvent({
22395
- id: key,
22396
- level: l?.level ?? null,
22397
- source
22398
- });
22399
- for (const cb of keyCallbacks) {
22400
- try {
22401
- cb(keyEvent);
22402
- } catch {
22403
- }
22404
- }
22405
- }
22406
- }
22509
+ const preResolved = { ...this._resolvedLevelStore };
22510
+ this._loggersCache = this._buildLoggersCache(serverLoggers);
22511
+ this._groupsCache = this._buildGroupsCache(serverGroups);
22512
+ this._resolvedLevelStore = {};
22513
+ this._applyLevels();
22514
+ this._fireDeltas(preResolved, this._resolvedLevelStore, source);
22407
22515
  }
22408
22516
  // ------------------------------------------------------------------
22409
22517
  // Internal: single-resource fetchers