@smplkit/sdk 3.0.49 → 3.0.51

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
  }
@@ -19288,12 +19291,10 @@ var LoggerChangeEvent = class {
19288
19291
  id;
19289
19292
  source;
19290
19293
  level;
19291
- deleted;
19292
19294
  constructor(fields) {
19293
19295
  this.id = fields.id;
19294
19296
  this.source = fields.source;
19295
- if (fields.level !== void 0) this.level = fields.level;
19296
- if (fields.deleted) this.deleted = fields.deleted;
19297
+ this.level = fields.level;
19297
19298
  Object.freeze(this);
19298
19299
  }
19299
19300
  };
@@ -21774,6 +21775,72 @@ var FlagsClient = class {
21774
21775
 
21775
21776
  // src/logging/client.ts
21776
21777
  var import_openapi_fetch5 = __toESM(require("openapi-fetch"), 1);
21778
+
21779
+ // src/logging/_resolution.ts
21780
+ var FALLBACK_LEVEL = "INFO";
21781
+ function resolveLevel(loggerId, environment, loggers, groups) {
21782
+ const direct = _resolveForEntry(loggerId, environment, loggers, groups);
21783
+ if (direct !== null) {
21784
+ if (isDebugEnabled()) {
21785
+ const source = _findResolutionSource(loggerId, environment, loggers, groups);
21786
+ debug("resolution", `${loggerId} -> ${direct} (source: ${source})`);
21787
+ }
21788
+ return direct;
21789
+ }
21790
+ const parts = loggerId.split(".");
21791
+ for (let i = parts.length - 1; i > 0; i--) {
21792
+ const ancestorId = parts.slice(0, i).join(".");
21793
+ const result = _resolveForEntry(ancestorId, environment, loggers, groups);
21794
+ if (result !== null) {
21795
+ debug("resolution", `${loggerId} -> ${result} (source: ancestor "${ancestorId}")`);
21796
+ return result;
21797
+ }
21798
+ }
21799
+ debug("resolution", `${loggerId} -> ${FALLBACK_LEVEL} (source: system default)`);
21800
+ return FALLBACK_LEVEL;
21801
+ }
21802
+ function _resolveForEntry(loggerId, environment, loggers, groups) {
21803
+ const entry = loggers[loggerId];
21804
+ if (entry === void 0) return null;
21805
+ const envLevel = _envLevel(entry.environments, environment);
21806
+ if (envLevel !== null) return envLevel;
21807
+ if (entry.level !== null && entry.level !== void 0) return entry.level;
21808
+ return _resolveGroupChain(entry.group, environment, groups);
21809
+ }
21810
+ function _findResolutionSource(loggerId, environment, loggers, groups) {
21811
+ const entry = loggers[loggerId];
21812
+ if (entry === void 0) return "not found";
21813
+ const envLevel = _envLevel(entry.environments, environment);
21814
+ if (envLevel !== null) return `env override "${environment}"`;
21815
+ if (entry.level !== null && entry.level !== void 0) return "base level";
21816
+ const groupResult = _resolveGroupChain(entry.group, environment, groups);
21817
+ if (groupResult !== null) return `group "${entry.group}"`;
21818
+ return "unknown";
21819
+ }
21820
+ function _resolveGroupChain(groupId, environment, groups) {
21821
+ const visited = /* @__PURE__ */ new Set();
21822
+ let currentId = groupId;
21823
+ while (currentId !== null && currentId !== void 0 && !visited.has(currentId)) {
21824
+ visited.add(currentId);
21825
+ const group = groups[currentId];
21826
+ if (group === void 0) break;
21827
+ const envLevel = _envLevel(group.environments, environment);
21828
+ if (envLevel !== null) return envLevel;
21829
+ if (group.level !== null && group.level !== void 0) return group.level;
21830
+ currentId = group.group;
21831
+ }
21832
+ return null;
21833
+ }
21834
+ function _envLevel(envs, environment) {
21835
+ if (!envs || typeof envs !== "object") return null;
21836
+ const envData = envs[environment];
21837
+ if (!envData || typeof envData !== "object") return null;
21838
+ const level = envData.level;
21839
+ if (level === null || level === void 0) return null;
21840
+ return level;
21841
+ }
21842
+
21843
+ // src/logging/client.ts
21777
21844
  var LOGGING_BASE_URL = "https://logging.smplkit.com";
21778
21845
  var LoggerRegistrationBuffer2 = class {
21779
21846
  _seen = /* @__PURE__ */ new Set();
@@ -21815,11 +21882,20 @@ var LoggingClient = class {
21815
21882
  _explicitAdapters = false;
21816
21883
  _loggerBuffer = new LoggerRegistrationBuffer2();
21817
21884
  _loggerFlushTimer = null;
21818
- // Local stores for diff-based listener firing
21819
- _loggerStore = {};
21820
- // key -> level
21821
- _groupStore = {};
21822
- // key -> level
21885
+ // Caches consulted by the resolution algorithm. The runtime client mutates
21886
+ // these from install(), refresh(), and the WebSocket handlers; resolveLevel
21887
+ // reads them on every apply.
21888
+ _loggersCache = {};
21889
+ _groupsCache = {};
21890
+ // Resolved-level snapshot, used to decide whether to fire change listeners.
21891
+ // Keyed by logger id; storing the *resolved* level (not raw `logger.level`)
21892
+ // means a group-driven level shift fires listeners even when the logger's
21893
+ // own level is untouched.
21894
+ _resolvedLevelStore = {};
21895
+ // Adapter-known logger names. The adapter knows loggers by their original
21896
+ // name (e.g. `com.acme.payments`); the cache is keyed by the same string
21897
+ // since the TypeScript SDK does not normalize.
21898
+ _knownLoggerNames = /* @__PURE__ */ new Set();
21823
21899
  /** @internal */
21824
21900
  constructor(apiKey, ensureWs, timeout, baseUrl, extraHeaders) {
21825
21901
  this._apiKey = apiKey;
@@ -22012,6 +22088,7 @@ var LoggingClient = class {
22012
22088
  const loggers = adapter.discover();
22013
22089
  for (const { name, level } of loggers) {
22014
22090
  this._loggerBuffer.add(name, level, level, service, environment);
22091
+ this._knownLoggerNames.add(name);
22015
22092
  discoveredCount++;
22016
22093
  }
22017
22094
  } catch {
@@ -22044,13 +22121,9 @@ var LoggingClient = class {
22044
22121
  "api",
22045
22122
  `fetched ${serverLoggers.length} logger(s) and ${serverGroups.length} group(s) from server`
22046
22123
  );
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
- }
22124
+ this._loggersCache = this._buildLoggersCache(serverLoggers);
22125
+ this._groupsCache = this._buildGroupsCache(serverGroups);
22126
+ this._applyLevels();
22054
22127
  } catch {
22055
22128
  }
22056
22129
  this._wsManager = this._ensureWs();
@@ -22142,41 +22215,104 @@ var LoggingClient = class {
22142
22215
  }
22143
22216
  return adapters;
22144
22217
  }
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
- }
22156
- }
22157
- debug("resolution", `${logger.id} -> ${effectiveLevel}`);
22218
+ /**
22219
+ * Refresh resolved levels and apply them to adapter-known loggers.
22220
+ *
22221
+ * Walks every adapter-known logger name through {@link resolveLevel}
22222
+ * (env override → base → group chain → dot-notation ancestry → fallback)
22223
+ * and pushes the result to every registered adapter. Loggers whose own
22224
+ * `level` is `null` are still applied — that's the whole point of group
22225
+ * inheritance and dot-notation ancestry.
22226
+ *
22227
+ * The resolved-level snapshot lives in `_resolvedLevelStore` so callers
22228
+ * can diff pre-vs-post and fire change listeners on actual effective-
22229
+ * level deltas. The store is scoped to adapter-known names: listener
22230
+ * fanout pairs 1:1 with `adapter.applyLevel` calls, so a logger that
22231
+ * the adapter doesn't know about has no apply and no listener fire.
22232
+ * @internal
22233
+ */
22234
+ _applyLevels() {
22235
+ const environment = this._parent?._environment ?? "";
22236
+ const newResolved = {};
22237
+ for (const name of this._knownLoggerNames) {
22238
+ newResolved[name] = resolveLevel(name, environment, this._loggersCache, this._groupsCache);
22239
+ }
22240
+ this._resolvedLevelStore = newResolved;
22241
+ for (const name of this._knownLoggerNames) {
22242
+ const resolved = newResolved[name];
22158
22243
  const metrics = this._parent?._metrics;
22159
22244
  if (metrics) {
22160
- metrics.record("logging.level_changes", 1, "changes", { logger: logger.id });
22245
+ metrics.record("logging.level_changes", 1, "changes", { logger: name });
22161
22246
  }
22162
22247
  for (const adapter of this._adapters) {
22163
22248
  try {
22164
- debug("adapter", `setLevel(${logger.id}, ${effectiveLevel})`);
22165
- adapter.applyLevel(logger.id, effectiveLevel);
22249
+ debug("adapter", `setLevel(${name}, ${resolved})`);
22250
+ adapter.applyLevel(name, resolved);
22166
22251
  } catch {
22167
22252
  }
22168
22253
  }
22169
22254
  }
22170
22255
  }
22256
+ /** @internal Convert a server Logger list into the resolution cache. */
22257
+ _buildLoggersCache(loggers) {
22258
+ const out = {};
22259
+ for (const l of loggers) {
22260
+ if (l.id === null) continue;
22261
+ out[l.id] = this._loggerToCacheEntry(l);
22262
+ }
22263
+ return out;
22264
+ }
22265
+ /** @internal Convert a server LogGroup list into the resolution cache. */
22266
+ _buildGroupsCache(groups) {
22267
+ const out = {};
22268
+ for (const g of groups) {
22269
+ if (g.id === null) continue;
22270
+ out[g.id] = this._groupToCacheEntry(g);
22271
+ }
22272
+ return out;
22273
+ }
22274
+ /** @internal */
22275
+ _loggerToCacheEntry(l) {
22276
+ return {
22277
+ level: l.level ?? null,
22278
+ group: l.group ?? null,
22279
+ managed: l.managed ?? null,
22280
+ environments: l.environments ?? null
22281
+ };
22282
+ }
22283
+ /** @internal */
22284
+ _groupToCacheEntry(g) {
22285
+ return {
22286
+ level: g.level ?? null,
22287
+ group: g.group ?? null,
22288
+ environments: g.environments ?? null
22289
+ };
22290
+ }
22171
22291
  /** Called by adapter hooks when a new logger is created in the framework. */
22172
22292
  _onAdapterNewLogger(name, level) {
22173
22293
  debug("discovery", `new logger intercepted at runtime: ${name}`);
22174
22294
  const service = this._parent?._service ?? null;
22175
22295
  const environment = this._parent?._environment ?? null;
22176
22296
  this._loggerBuffer.add(name, level, level, service, environment);
22297
+ this._knownLoggerNames.add(name);
22177
22298
  if (this._loggerBuffer.pendingCount >= 50) {
22178
22299
  void this._flushLoggerBuffer();
22179
22300
  }
22301
+ if (this._started) {
22302
+ const resolved = resolveLevel(
22303
+ name,
22304
+ this._parent?._environment ?? "",
22305
+ this._loggersCache,
22306
+ this._groupsCache
22307
+ );
22308
+ this._resolvedLevelStore[name] = resolved;
22309
+ for (const adapter of this._adapters) {
22310
+ try {
22311
+ adapter.applyLevel(name, resolved);
22312
+ } catch {
22313
+ }
22314
+ }
22315
+ }
22180
22316
  }
22181
22317
  /** Flush buffered loggers to the bulk-register endpoint. */
22182
22318
  async _flushLoggerBuffer() {
@@ -22210,33 +22346,14 @@ var LoggingClient = class {
22210
22346
  const id = data.id;
22211
22347
  if (!id) return;
22212
22348
  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
- }
22349
+ const preResolved = { ...this._resolvedLevelStore };
22350
+ if (logger !== null) {
22351
+ this._loggersCache[id] = this._loggerToCacheEntry(logger);
22352
+ } else {
22353
+ delete this._loggersCache[id];
22239
22354
  }
22355
+ this._applyLevels();
22356
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket");
22240
22357
  }).catch((err) => {
22241
22358
  debug(
22242
22359
  "websocket",
@@ -22248,50 +22365,24 @@ var LoggingClient = class {
22248
22365
  debug("websocket", `logger_deleted event received: ${JSON.stringify(data)}`);
22249
22366
  const id = data.id;
22250
22367
  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
- }
22368
+ delete this._loggersCache[id];
22369
+ const preResolved = { ...this._resolvedLevelStore };
22370
+ this._applyLevels();
22371
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket", /* @__PURE__ */ new Set([id]));
22281
22372
  };
22282
22373
  _handleGroupChanged = (data) => {
22283
22374
  debug("websocket", `group_changed event received: ${JSON.stringify(data)}`);
22284
22375
  const id = data.id;
22285
22376
  if (!id) return;
22286
22377
  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
- });
22378
+ const preResolved = { ...this._resolvedLevelStore };
22379
+ if (group !== null) {
22380
+ this._groupsCache[id] = this._groupToCacheEntry(group);
22381
+ } else {
22382
+ delete this._groupsCache[id];
22383
+ }
22384
+ this._applyLevels();
22385
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket");
22295
22386
  }).catch((err) => {
22296
22387
  debug(
22297
22388
  "websocket",
@@ -22303,47 +22394,63 @@ var LoggingClient = class {
22303
22394
  debug("websocket", `group_deleted event received: ${JSON.stringify(data)}`);
22304
22395
  const id = data.id;
22305
22396
  if (!id) return;
22306
- delete this._groupStore[id];
22307
- const event = new LoggerChangeEvent({
22308
- id,
22309
- level: null,
22310
- source: "websocket",
22311
- deleted: true
22312
- });
22313
- for (const cb of this._globalListeners) {
22314
- try {
22315
- cb(event);
22316
- } catch (err) {
22317
- debug(
22318
- "websocket",
22319
- `group_deleted listener error: ${err instanceof Error ? err.message : String(err)}`
22320
- );
22321
- }
22322
- }
22323
- const idCallbacks = this._keyListeners.get(id);
22324
- if (idCallbacks) {
22325
- for (const cb of idCallbacks) {
22397
+ delete this._groupsCache[id];
22398
+ const preResolved = { ...this._resolvedLevelStore };
22399
+ this._applyLevels();
22400
+ this._fireDeltas(preResolved, this._resolvedLevelStore, "websocket", /* @__PURE__ */ new Set([id]));
22401
+ };
22402
+ /**
22403
+ * @internal Fire change listeners for every logger whose effective level
22404
+ * changed between `pre` and `post`.
22405
+ *
22406
+ * Contract:
22407
+ * - Iterates loggers present in `post` (the post-apply resolved store).
22408
+ * A key in `pre` but not in `post` is a cache eviction — that key
22409
+ * itself fires nothing. Dependents that re-resolved to a new value
22410
+ * are still in `post` and fire normally.
22411
+ * - For every changed logger, fires every global listener once with
22412
+ * that logger's own payload (no "summary" event), then fires every
22413
+ * key-scoped listener registered for that id.
22414
+ * - One adapter.applyLevel call ↔ one listener notification per
22415
+ * subscriber. A trigger that moves N loggers fires the global
22416
+ * listener N times, not once.
22417
+ * - `suppressIds` is the deletion-event escape hatch: a deleted id
22418
+ * fires nothing for itself even when its adapter-known resolved
22419
+ * level moved (e.g. fell back to INFO).
22420
+ */
22421
+ _fireDeltas(pre, post, source, suppressIds) {
22422
+ for (const id of Object.keys(post)) {
22423
+ if (suppressIds?.has(id)) continue;
22424
+ const next = post[id];
22425
+ if (pre[id] === next) continue;
22426
+ const event = new LoggerChangeEvent({ id, source, level: next });
22427
+ for (const cb of this._globalListeners) {
22326
22428
  try {
22327
22429
  cb(event);
22328
- } catch (err) {
22329
- debug(
22330
- "websocket",
22331
- `group_deleted key listener error: ${err instanceof Error ? err.message : String(err)}`
22332
- );
22430
+ } catch {
22431
+ }
22432
+ }
22433
+ const keyCallbacks = this._keyListeners.get(id);
22434
+ if (keyCallbacks) {
22435
+ for (const cb of keyCallbacks) {
22436
+ try {
22437
+ cb(event);
22438
+ } catch {
22439
+ }
22333
22440
  }
22334
22441
  }
22335
22442
  }
22336
- };
22443
+ }
22337
22444
  _handleLoggersChanged = (_data) => {
22338
22445
  debug("websocket", `loggers_changed event received`);
22339
22446
  void this._resolveAndFire("websocket").catch(() => {
22340
22447
  });
22341
22448
  };
22342
22449
  /**
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.
22450
+ * Full refetch of loggers + log_groups, rebuild the caches, re-resolve
22451
+ * every adapter-known logger, and fire change listeners on resolved-level
22452
+ * deltas. Shared between the `loggers_changed` WS handler and the
22453
+ * public `refresh()` method.
22347
22454
  * @internal
22348
22455
  */
22349
22456
  async _resolveAndFire(source) {
@@ -22352,58 +22459,12 @@ var LoggingClient = class {
22352
22459
  this._listLogGroups()
22353
22460
  ]);
22354
22461
  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
- }
22462
+ const preResolved = { ...this._resolvedLevelStore };
22463
+ this._loggersCache = this._buildLoggersCache(serverLoggers);
22464
+ this._groupsCache = this._buildGroupsCache(serverGroups);
22465
+ this._resolvedLevelStore = {};
22466
+ this._applyLevels();
22467
+ this._fireDeltas(preResolved, this._resolvedLevelStore, source);
22407
22468
  }
22408
22469
  // ------------------------------------------------------------------
22409
22470
  // Internal: single-resource fetchers